/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_dirstate.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""Tests of the dirstate functionality being built for WorkingTreeFormat4."""
18
18
 
19
 
import bisect
20
19
import os
 
20
import tempfile
21
21
 
22
 
from bzrlib import (
23
 
    dirstate,
 
22
from .. import (
 
23
    controldir,
24
24
    errors,
25
 
    inventory,
26
25
    memorytree,
27
26
    osutils,
28
27
    revision as _mod_revision,
 
28
    revisiontree,
29
29
    tests,
30
30
    )
31
 
from bzrlib.tests import test_osutils
 
31
from ..bzr import (
 
32
    dirstate,
 
33
    inventory,
 
34
    inventorytree,
 
35
    workingtree_4,
 
36
    )
 
37
from . import (
 
38
    features,
 
39
    test_osutils,
 
40
    )
 
41
from .scenarios import load_tests_apply_scenarios
32
42
 
33
43
 
34
44
# TODO:
44
54
# set_path_id  setting id when state is in memory modified
45
55
 
46
56
 
47
 
def load_tests(basic_tests, module, loader):
48
 
    suite = loader.suiteClass()
49
 
    dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
50
 
        basic_tests, tests.condition_isinstance(TestCaseWithDirState))
51
 
    tests.multiply_tests(dir_reader_tests,
52
 
                         test_osutils.dir_reader_scenarios(), suite)
53
 
    suite.addTest(remaining_tests)
54
 
    return suite
 
57
class TestErrors(tests.TestCase):
 
58
 
 
59
    def test_dirstate_corrupt(self):
 
60
        error = dirstate.DirstateCorrupt('.bzr/checkout/dirstate',
 
61
                                         'trailing garbage: "x"')
 
62
        self.assertEqualDiff("The dirstate file (.bzr/checkout/dirstate)"
 
63
                             " appears to be corrupt: trailing garbage: \"x\"",
 
64
                             str(error))
 
65
 
 
66
 
 
67
load_tests = load_tests_apply_scenarios
55
68
 
56
69
 
57
70
class TestCaseWithDirState(tests.TestCaseWithTransport):
58
71
    """Helper functions for creating DirState objects with various content."""
59
72
 
 
73
    scenarios = test_osutils.dir_reader_scenarios()
 
74
 
60
75
    # Set by load_tests
61
76
    _dir_reader_class = None
62
 
    _native_to_unicode = None # Not used yet
 
77
    _native_to_unicode = None  # Not used yet
63
78
 
64
79
    def setUp(self):
65
 
        tests.TestCaseWithTransport.setUp(self)
66
 
 
 
80
        super(TestCaseWithDirState, self).setUp()
67
81
        self.overrideAttr(osutils,
68
82
                          '_selected_dir_reader', self._dir_reader_class())
69
83
 
74
88
 
75
89
    def create_dirstate_with_root(self):
76
90
        """Return a write-locked state with a single root entry."""
77
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
78
 
        root_entry_direntry = ('', '', 'a-root-value'), [
79
 
            ('d', '', 0, False, packed_stat),
 
91
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
92
        root_entry_direntry = (b'', b'', b'a-root-value'), [
 
93
            (b'd', b'', 0, False, packed_stat),
80
94
            ]
81
95
        dirblocks = []
82
 
        dirblocks.append(('', [root_entry_direntry]))
83
 
        dirblocks.append(('', []))
 
96
        dirblocks.append((b'', [root_entry_direntry]))
 
97
        dirblocks.append((b'', []))
84
98
        state = self.create_empty_dirstate()
85
99
        try:
86
100
            state._set_data([], dirblocks)
92
106
 
93
107
    def create_dirstate_with_root_and_subdir(self):
94
108
        """Return a locked DirState with a root and a subdir"""
95
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
96
 
        subdir_entry = ('', 'subdir', 'subdir-id'), [
97
 
            ('d', '', 0, False, packed_stat),
 
109
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
110
        subdir_entry = (b'', b'subdir', b'subdir-id'), [
 
111
            (b'd', b'', 0, False, packed_stat),
98
112
            ]
99
113
        state = self.create_dirstate_with_root()
100
114
        try:
123
137
 
124
138
        :return: The dirstate, still write-locked.
125
139
        """
126
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
127
 
        null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
128
 
        root_entry = ('', '', 'a-root-value'), [
129
 
            ('d', '', 0, False, packed_stat),
130
 
            ]
131
 
        a_entry = ('', 'a', 'a-dir'), [
132
 
            ('d', '', 0, False, packed_stat),
133
 
            ]
134
 
        b_entry = ('', 'b', 'b-dir'), [
135
 
            ('d', '', 0, False, packed_stat),
136
 
            ]
137
 
        c_entry = ('', 'c', 'c-file'), [
138
 
            ('f', null_sha, 10, False, packed_stat),
139
 
            ]
140
 
        d_entry = ('', 'd', 'd-file'), [
141
 
            ('f', null_sha, 20, False, packed_stat),
142
 
            ]
143
 
        e_entry = ('a', 'e', 'e-dir'), [
144
 
            ('d', '', 0, False, packed_stat),
145
 
            ]
146
 
        f_entry = ('a', 'f', 'f-file'), [
147
 
            ('f', null_sha, 30, False, packed_stat),
148
 
            ]
149
 
        g_entry = ('b', 'g', 'g-file'), [
150
 
            ('f', null_sha, 30, False, packed_stat),
151
 
            ]
152
 
        h_entry = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file'), [
153
 
            ('f', null_sha, 40, False, packed_stat),
 
140
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
141
        null_sha = b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
 
142
        root_entry = (b'', b'', b'a-root-value'), [
 
143
            (b'd', b'', 0, False, packed_stat),
 
144
            ]
 
145
        a_entry = (b'', b'a', b'a-dir'), [
 
146
            (b'd', b'', 0, False, packed_stat),
 
147
            ]
 
148
        b_entry = (b'', b'b', b'b-dir'), [
 
149
            (b'd', b'', 0, False, packed_stat),
 
150
            ]
 
151
        c_entry = (b'', b'c', b'c-file'), [
 
152
            (b'f', null_sha, 10, False, packed_stat),
 
153
            ]
 
154
        d_entry = (b'', b'd', b'd-file'), [
 
155
            (b'f', null_sha, 20, False, packed_stat),
 
156
            ]
 
157
        e_entry = (b'a', b'e', b'e-dir'), [
 
158
            (b'd', b'', 0, False, packed_stat),
 
159
            ]
 
160
        f_entry = (b'a', b'f', b'f-file'), [
 
161
            (b'f', null_sha, 30, False, packed_stat),
 
162
            ]
 
163
        g_entry = (b'b', b'g', b'g-file'), [
 
164
            (b'f', null_sha, 30, False, packed_stat),
 
165
            ]
 
166
        h_entry = (b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file'), [
 
167
            (b'f', null_sha, 40, False, packed_stat),
154
168
            ]
155
169
        dirblocks = []
156
 
        dirblocks.append(('', [root_entry]))
157
 
        dirblocks.append(('', [a_entry, b_entry, c_entry, d_entry]))
158
 
        dirblocks.append(('a', [e_entry, f_entry]))
159
 
        dirblocks.append(('b', [g_entry, h_entry]))
 
170
        dirblocks.append((b'', [root_entry]))
 
171
        dirblocks.append((b'', [a_entry, b_entry, c_entry, d_entry]))
 
172
        dirblocks.append((b'a', [e_entry, f_entry]))
 
173
        dirblocks.append((b'b', [g_entry, h_entry]))
160
174
        state = dirstate.DirState.initialize('dirstate')
161
175
        state._validate()
162
176
        try:
182
196
        # some operation to get here.
183
197
        self.assertTrue(state._lock_token is not None)
184
198
        try:
185
 
            self.assertEqual(expected_result[0],  state.get_parent_ids())
 
199
            self.assertEqual(expected_result[0], state.get_parent_ids())
186
200
            # there should be no ghosts in this tree.
187
201
            self.assertEqual([], state.get_ghosts())
188
202
            # there should be one fileid in this tree - the root of the tree.
211
225
        """
212
226
        tree = self.make_branch_and_tree('tree')
213
227
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'b-c', 'f']
214
 
        file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'b-c-id', 'f-id']
 
228
        file_ids = [b'a-id', b'b-id', b'c-id',
 
229
                    b'd-id', b'e-id', b'b-c-id', b'f-id']
215
230
        self.build_tree(['tree/' + p for p in paths])
216
 
        tree.set_root_id('TREE_ROOT')
 
231
        tree.set_root_id(b'TREE_ROOT')
217
232
        tree.add([p.rstrip('/') for p in paths], file_ids)
218
 
        tree.commit('initial', rev_id='rev-1')
219
 
        revision_id = 'rev-1'
 
233
        tree.commit('initial', rev_id=b'rev-1')
 
234
        revision_id = b'rev-1'
220
235
        # a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
221
236
        t = self.get_transport('tree')
222
237
        a_text = t.get_bytes('a')
241
256
        f_len = len(f_text)
242
257
        null_stat = dirstate.DirState.NULLSTAT
243
258
        expected = {
244
 
            '':(('', '', 'TREE_ROOT'), [
245
 
                  ('d', '', 0, False, null_stat),
246
 
                  ('d', '', 0, False, revision_id),
247
 
                ]),
248
 
            'a':(('', 'a', 'a-id'), [
249
 
                   ('f', '', 0, False, null_stat),
250
 
                   ('f', a_sha, a_len, False, revision_id),
251
 
                 ]),
252
 
            'b':(('', 'b', 'b-id'), [
253
 
                  ('d', '', 0, False, null_stat),
254
 
                  ('d', '', 0, False, revision_id),
255
 
                 ]),
256
 
            'b/c':(('b', 'c', 'c-id'), [
257
 
                    ('f', '', 0, False, null_stat),
258
 
                    ('f', c_sha, c_len, False, revision_id),
259
 
                   ]),
260
 
            'b/d':(('b', 'd', 'd-id'), [
261
 
                    ('d', '', 0, False, null_stat),
262
 
                    ('d', '', 0, False, revision_id),
263
 
                   ]),
264
 
            'b/d/e':(('b/d', 'e', 'e-id'), [
265
 
                      ('f', '', 0, False, null_stat),
266
 
                      ('f', e_sha, e_len, False, revision_id),
267
 
                     ]),
268
 
            'b-c':(('', 'b-c', 'b-c-id'), [
269
 
                      ('f', '', 0, False, null_stat),
270
 
                      ('f', b_c_sha, b_c_len, False, revision_id),
271
 
                     ]),
272
 
            'f':(('', 'f', 'f-id'), [
273
 
                  ('f', '', 0, False, null_stat),
274
 
                  ('f', f_sha, f_len, False, revision_id),
275
 
                 ]),
 
259
            b'': ((b'', b'', b'TREE_ROOT'), [
 
260
                  (b'd', b'', 0, False, null_stat),
 
261
                  (b'd', b'', 0, False, revision_id),
 
262
                  ]),
 
263
            b'a': ((b'', b'a', b'a-id'), [
 
264
                   (b'f', b'', 0, False, null_stat),
 
265
                   (b'f', a_sha, a_len, False, revision_id),
 
266
                   ]),
 
267
            b'b': ((b'', b'b', b'b-id'), [
 
268
                  (b'd', b'', 0, False, null_stat),
 
269
                  (b'd', b'', 0, False, revision_id),
 
270
                ]),
 
271
            b'b/c': ((b'b', b'c', b'c-id'), [
 
272
                    (b'f', b'', 0, False, null_stat),
 
273
                    (b'f', c_sha, c_len, False, revision_id),
 
274
                ]),
 
275
            b'b/d': ((b'b', b'd', b'd-id'), [
 
276
                    (b'd', b'', 0, False, null_stat),
 
277
                    (b'd', b'', 0, False, revision_id),
 
278
                ]),
 
279
            b'b/d/e': ((b'b/d', b'e', b'e-id'), [
 
280
                      (b'f', b'', 0, False, null_stat),
 
281
                      (b'f', e_sha, e_len, False, revision_id),
 
282
                ]),
 
283
            b'b-c': ((b'', b'b-c', b'b-c-id'), [
 
284
                (b'f', b'', 0, False, null_stat),
 
285
                (b'f', b_c_sha, b_c_len, False, revision_id),
 
286
                ]),
 
287
            b'f': ((b'', b'f', b'f-id'), [
 
288
                (b'f', b'', 0, False, null_stat),
 
289
                (b'f', f_sha, f_len, False, revision_id),
 
290
                ]),
276
291
        }
277
292
        state = dirstate.DirState.from_tree(tree, 'dirstate')
278
293
        try:
301
316
        tree, state, expected = self.create_basic_dirstate()
302
317
        # Now we will just remove and add every file so we get an extra entry
303
318
        # per entry. Unversion in reverse order so we handle subdirs
304
 
        tree.unversion(['f-id', 'b-c-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
 
319
        tree.unversion(['f', 'b-c', 'b/d/e', 'b/d', 'b/c', 'b', 'a'])
305
320
        tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f'],
306
 
                 ['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'b-c-id2', 'f-id2'])
 
321
                 [b'a-id2', b'b-id2', b'c-id2', b'd-id2', b'e-id2', b'b-c-id2', b'f-id2'])
307
322
 
308
323
        # Update the expected dictionary.
309
 
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f']:
 
324
        for path in [b'a', b'b', b'b/c', b'b/d', b'b/d/e', b'b-c', b'f']:
310
325
            orig = expected[path]
311
 
            path2 = path + '2'
 
326
            path2 = path + b'2'
312
327
            # This record was deleted in the current tree
313
328
            expected[path] = (orig[0], [dirstate.DirState.NULL_PARENT_DETAILS,
314
329
                                        orig[1][1]])
315
 
            new_key = (orig[0][0], orig[0][1], orig[0][2]+'2')
 
330
            new_key = (orig[0][0], orig[0][1], orig[0][2] + b'2')
316
331
            # And didn't exist in the basis tree
317
332
            expected[path2] = (new_key, [orig[1][0],
318
333
                                         dirstate.DirState.NULL_PARENT_DETAILS])
343
358
        # And a directory
344
359
        tree.rename_one('b/d', 'h')
345
360
 
346
 
        old_a = expected['a']
347
 
        expected['a'] = (old_a[0], [('r', 'b/g', 0, False, ''), old_a[1][1]])
348
 
        expected['b/g'] = (('b', 'g', 'a-id'), [old_a[1][0],
349
 
                                                ('r', 'a', 0, False, '')])
350
 
        old_d = expected['b/d']
351
 
        expected['b/d'] = (old_d[0], [('r', 'h', 0, False, ''), old_d[1][1]])
352
 
        expected['h'] = (('', 'h', 'd-id'), [old_d[1][0],
353
 
                                             ('r', 'b/d', 0, False, '')])
 
361
        old_a = expected[b'a']
 
362
        expected[b'a'] = (
 
363
            old_a[0], [(b'r', b'b/g', 0, False, b''), old_a[1][1]])
 
364
        expected[b'b/g'] = ((b'b', b'g', b'a-id'), [old_a[1][0],
 
365
                                                    (b'r', b'a', 0, False, b'')])
 
366
        old_d = expected[b'b/d']
 
367
        expected[b'b/d'] = (old_d[0],
 
368
                            [(b'r', b'h', 0, False, b''), old_d[1][1]])
 
369
        expected[b'h'] = ((b'', b'h', b'd-id'), [old_d[1][0],
 
370
                                                 (b'r', b'b/d', 0, False, b'')])
354
371
 
355
 
        old_e = expected['b/d/e']
356
 
        expected['b/d/e'] = (old_e[0], [('r', 'h/e', 0, False, ''),
357
 
                             old_e[1][1]])
358
 
        expected['h/e'] = (('h', 'e', 'e-id'), [old_e[1][0],
359
 
                                                ('r', 'b/d/e', 0, False, '')])
 
372
        old_e = expected[b'b/d/e']
 
373
        expected[b'b/d/e'] = (old_e[0], [(b'r', b'h/e', 0, False, b''),
 
374
                                         old_e[1][1]])
 
375
        expected[b'h/e'] = ((b'h', b'e', b'e-id'), [old_e[1][0],
 
376
                                                    (b'r', b'b/d/e', 0, False, b'')])
360
377
 
361
378
        state.unlock()
362
379
        try:
377
394
        # There are no files on disk and no parents
378
395
        tree = self.make_branch_and_tree('tree')
379
396
        expected_result = ([], [
380
 
            (('', '', tree.get_root_id()), # common details
381
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
382
 
             ])])
 
397
            ((b'', b'', tree.path2id('')),  # common details
 
398
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
399
              ])])
383
400
        state = dirstate.DirState.from_tree(tree, 'dirstate')
384
401
        state._validate()
385
402
        self.check_state_with_reopen(expected_result, state)
387
404
    def test_1_parents_empty_to_dirstate(self):
388
405
        # create a parent by doing a commit
389
406
        tree = self.make_branch_and_tree('tree')
390
 
        rev_id = tree.commit('first post').encode('utf8')
 
407
        rev_id = tree.commit('first post')
391
408
        root_stat_pack = dirstate.pack_stat(os.stat(tree.basedir))
392
409
        expected_result = ([rev_id], [
393
 
            (('', '', tree.get_root_id()), # common details
394
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
395
 
              ('d', '', 0, False, rev_id), # first parent details
396
 
             ])])
 
410
            ((b'', b'', tree.path2id('')),  # common details
 
411
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
412
              (b'd', b'', 0, False, rev_id),  # first parent details
 
413
              ])])
397
414
        state = dirstate.DirState.from_tree(tree, 'dirstate')
398
415
        self.check_state_with_reopen(expected_result, state)
399
416
        state.lock_read()
406
423
        # create a parent by doing a commit
407
424
        tree = self.make_branch_and_tree('tree')
408
425
        rev_id = tree.commit('first post')
409
 
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
426
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
410
427
        rev_id2 = tree2.commit('second post', allow_pointless=True)
411
428
        tree.merge_from_branch(tree2.branch)
412
429
        expected_result = ([rev_id, rev_id2], [
413
 
            (('', '', tree.get_root_id()), # common details
414
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
415
 
              ('d', '', 0, False, rev_id), # first parent details
416
 
              ('d', '', 0, False, rev_id), # second parent details
417
 
             ])])
 
430
            ((b'', b'', tree.path2id('')),  # common details
 
431
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
432
              (b'd', b'', 0, False, rev_id),  # first parent details
 
433
              (b'd', b'', 0, False, rev_id),  # second parent details
 
434
              ])])
418
435
        state = dirstate.DirState.from_tree(tree, 'dirstate')
419
436
        self.check_state_with_reopen(expected_result, state)
420
437
        state.lock_read()
429
446
        tree = self.make_branch_and_tree('tree')
430
447
        self.build_tree(['tree/unknown'])
431
448
        expected_result = ([], [
432
 
            (('', '', tree.get_root_id()), # common details
433
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
434
 
             ])])
 
449
            ((b'', b'', tree.path2id('')),  # common details
 
450
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
451
              ])])
435
452
        state = dirstate.DirState.from_tree(tree, 'dirstate')
436
453
        self.check_state_with_reopen(expected_result, state)
437
454
 
438
455
    def get_tree_with_a_file(self):
439
456
        tree = self.make_branch_and_tree('tree')
440
457
        self.build_tree(['tree/a file'])
441
 
        tree.add('a file', 'a-file-id')
 
458
        tree.add('a file', b'a-file-id')
442
459
        return tree
443
460
 
444
461
    def test_non_empty_no_parents_to_dirstate(self):
446
463
        # There are files on disk and no parents
447
464
        tree = self.get_tree_with_a_file()
448
465
        expected_result = ([], [
449
 
            (('', '', tree.get_root_id()), # common details
450
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
451
 
             ]),
452
 
            (('', 'a file', 'a-file-id'), # common
453
 
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
454
 
             ]),
 
466
            ((b'', b'', tree.path2id('')),  # common details
 
467
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
468
              ]),
 
469
            ((b'', b'a file', b'a-file-id'),  # common
 
470
             [(b'f', b'', 0, False, dirstate.DirState.NULLSTAT),  # current
 
471
              ]),
455
472
            ])
456
473
        state = dirstate.DirState.from_tree(tree, 'dirstate')
457
474
        self.check_state_with_reopen(expected_result, state)
459
476
    def test_1_parents_not_empty_to_dirstate(self):
460
477
        # create a parent by doing a commit
461
478
        tree = self.get_tree_with_a_file()
462
 
        rev_id = tree.commit('first post').encode('utf8')
 
479
        rev_id = tree.commit('first post')
463
480
        # change the current content to be different this will alter stat, sha
464
481
        # and length:
465
 
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
482
        self.build_tree_contents([('tree/a file', b'new content\n')])
466
483
        expected_result = ([rev_id], [
467
 
            (('', '', tree.get_root_id()), # common details
468
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
469
 
              ('d', '', 0, False, rev_id), # first parent details
470
 
             ]),
471
 
            (('', 'a file', 'a-file-id'), # common
472
 
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
473
 
              ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
474
 
               rev_id), # first parent
475
 
             ]),
 
484
            ((b'', b'', tree.path2id('')),  # common details
 
485
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
486
              (b'd', b'', 0, False, rev_id),  # first parent details
 
487
              ]),
 
488
            ((b'', b'a file', b'a-file-id'),  # common
 
489
             [(b'f', b'', 0, False, dirstate.DirState.NULLSTAT),  # current
 
490
              (b'f', b'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
 
491
               rev_id),  # first parent
 
492
              ]),
476
493
            ])
477
494
        state = dirstate.DirState.from_tree(tree, 'dirstate')
478
495
        self.check_state_with_reopen(expected_result, state)
480
497
    def test_2_parents_not_empty_to_dirstate(self):
481
498
        # create a parent by doing a commit
482
499
        tree = self.get_tree_with_a_file()
483
 
        rev_id = tree.commit('first post').encode('utf8')
484
 
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
500
        rev_id = tree.commit('first post')
 
501
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
485
502
        # change the current content to be different this will alter stat, sha
486
503
        # and length:
487
 
        self.build_tree_contents([('tree2/a file', 'merge content\n')])
488
 
        rev_id2 = tree2.commit('second post').encode('utf8')
 
504
        self.build_tree_contents([('tree2/a file', b'merge content\n')])
 
505
        rev_id2 = tree2.commit('second post')
489
506
        tree.merge_from_branch(tree2.branch)
490
507
        # change the current content to be different this will alter stat, sha
491
508
        # and length again, giving us three distinct values:
492
 
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
509
        self.build_tree_contents([('tree/a file', b'new content\n')])
493
510
        expected_result = ([rev_id, rev_id2], [
494
 
            (('', '', tree.get_root_id()), # common details
495
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
496
 
              ('d', '', 0, False, rev_id), # first parent details
497
 
              ('d', '', 0, False, rev_id), # second parent details
498
 
             ]),
499
 
            (('', 'a file', 'a-file-id'), # common
500
 
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
501
 
              ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
502
 
               rev_id), # first parent
503
 
              ('f', '314d796174c9412647c3ce07dfb5d36a94e72958', 14, False,
504
 
               rev_id2), # second parent
505
 
             ]),
 
511
            ((b'', b'', tree.path2id('')),  # common details
 
512
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
513
              (b'd', b'', 0, False, rev_id),  # first parent details
 
514
              (b'd', b'', 0, False, rev_id),  # second parent details
 
515
              ]),
 
516
            ((b'', b'a file', b'a-file-id'),  # common
 
517
             [(b'f', b'', 0, False, dirstate.DirState.NULLSTAT),  # current
 
518
              (b'f', b'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
 
519
               rev_id),  # first parent
 
520
              (b'f', b'314d796174c9412647c3ce07dfb5d36a94e72958', 14, False,
 
521
               rev_id2),  # second parent
 
522
              ]),
506
523
            ])
507
524
        state = dirstate.DirState.from_tree(tree, 'dirstate')
508
525
        self.check_state_with_reopen(expected_result, state)
515
532
        parents = []
516
533
        for i in range(7):
517
534
            tree = self.make_branch_and_tree('tree%d' % i)
518
 
            self.build_tree(['tree%d/name' % i,])
519
 
            tree.add(['name'], ['file-id%d' % i])
520
 
            revision_id = 'revid-%d' % i
 
535
            self.build_tree(['tree%d/name' % i, ])
 
536
            tree.add(['name'], [b'file-id%d' % i])
 
537
            revision_id = b'revid-%d' % i
521
538
            tree.commit('message', rev_id=revision_id)
522
539
            parents.append((revision_id,
523
 
                tree.branch.repository.revision_tree(revision_id)))
 
540
                            tree.branch.repository.revision_tree(revision_id)))
524
541
        # now fold these trees into a dirstate
525
542
        state = dirstate.DirState.initialize('dirstate')
526
543
        try:
532
549
 
533
550
class TestDirStateOnFile(TestCaseWithDirState):
534
551
 
 
552
    def create_updated_dirstate(self):
 
553
        self.build_tree(['a-file'])
 
554
        tree = self.make_branch_and_tree('.')
 
555
        tree.add(['a-file'], [b'a-id'])
 
556
        tree.commit('add a-file')
 
557
        # Save and unlock the state, re-open it in readonly mode
 
558
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
559
        state.save()
 
560
        state.unlock()
 
561
        state = dirstate.DirState.on_file('dirstate')
 
562
        state.lock_read()
 
563
        return state
 
564
 
535
565
    def test_construct_with_path(self):
536
566
        tree = self.make_branch_and_tree('tree')
537
567
        state = dirstate.DirState.from_tree(tree, 'dirstate.from_tree')
539
569
        # write to disk.
540
570
        lines = state.get_lines()
541
571
        state.unlock()
542
 
        self.build_tree_contents([('dirstate', ''.join(lines))])
 
572
        self.build_tree_contents([('dirstate', b''.join(lines))])
543
573
        # get a state object
544
574
        # no parents, default tree content
545
575
        expected_result = ([], [
546
 
            (('', '', tree.get_root_id()), # common details
 
576
            ((b'', b'', tree.path2id('')),  # common details
547
577
             # current tree details, but new from_tree skips statting, it
548
578
             # uses set_state_from_inventory, and thus depends on the
549
579
             # inventory state.
550
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT),
551
 
             ])
 
580
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),
 
581
              ])
552
582
            ])
553
583
        state = dirstate.DirState.on_file('dirstate')
554
 
        state.lock_write() # check_state_with_reopen will save() and unlock it
 
584
        state.lock_write()  # check_state_with_reopen will save() and unlock it
555
585
        self.check_state_with_reopen(expected_result, state)
556
586
 
557
587
    def test_can_save_clean_on_file(self):
566
596
            state.unlock()
567
597
 
568
598
    def test_can_save_in_read_lock(self):
569
 
        self.build_tree(['a-file'])
570
 
        state = dirstate.DirState.initialize('dirstate')
571
 
        try:
572
 
            # No stat and no sha1 sum.
573
 
            state.add('a-file', 'a-file-id', 'file', None, '')
574
 
            state.save()
575
 
        finally:
576
 
            state.unlock()
577
 
 
578
 
        # Now open in readonly mode
579
 
        state = dirstate.DirState.on_file('dirstate')
580
 
        state.lock_read()
581
 
        try:
582
 
            entry = state._get_entry(0, path_utf8='a-file')
 
599
        state = self.create_updated_dirstate()
 
600
        try:
 
601
            entry = state._get_entry(0, path_utf8=b'a-file')
583
602
            # The current size should be 0 (default)
584
603
            self.assertEqual(0, entry[1][0][2])
585
604
            # We should have a real entry.
586
605
            self.assertNotEqual((None, None), entry)
587
 
            # Make sure everything is old enough
 
606
            # Set the cutoff-time into the future, so things look cacheable
588
607
            state._sha_cutoff_time()
589
 
            state._cutoff_time += 10
590
 
            # Change the file length
591
 
            self.build_tree_contents([('a-file', 'shorter')])
592
 
            sha1sum = dirstate.update_entry(state, entry, 'a-file',
593
 
                os.lstat('a-file'))
594
 
            # new file, no cached sha:
595
 
            self.assertEqual(None, sha1sum)
 
608
            state._cutoff_time += 10.0
 
609
            st = os.lstat('a-file')
 
610
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
611
            # We updated the current sha1sum because the file is cacheable
 
612
            self.assertEqual(b'ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
613
                             sha1sum)
596
614
 
597
615
            # The dirblock has been updated
598
 
            self.assertEqual(7, entry[1][0][2])
599
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
616
            self.assertEqual(st.st_size, entry[1][0][2])
 
617
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
600
618
                             state._dirblock_state)
601
619
 
602
620
            del entry
610
628
        state = dirstate.DirState.on_file('dirstate')
611
629
        state.lock_read()
612
630
        try:
613
 
            entry = state._get_entry(0, path_utf8='a-file')
614
 
            self.assertEqual(7, entry[1][0][2])
 
631
            entry = state._get_entry(0, path_utf8=b'a-file')
 
632
            self.assertEqual(st.st_size, entry[1][0][2])
615
633
        finally:
616
634
            state.unlock()
617
635
 
618
636
    def test_save_fails_quietly_if_locked(self):
619
637
        """If dirstate is locked, save will fail without complaining."""
620
 
        self.build_tree(['a-file'])
621
 
        state = dirstate.DirState.initialize('dirstate')
622
 
        try:
623
 
            # No stat and no sha1 sum.
624
 
            state.add('a-file', 'a-file-id', 'file', None, '')
625
 
            state.save()
626
 
        finally:
627
 
            state.unlock()
628
 
 
629
 
        state = dirstate.DirState.on_file('dirstate')
630
 
        state.lock_read()
631
 
        try:
632
 
            entry = state._get_entry(0, path_utf8='a-file')
633
 
            sha1sum = dirstate.update_entry(state, entry, 'a-file',
634
 
                os.lstat('a-file'))
635
 
            # No sha - too new
636
 
            self.assertEqual(None, sha1sum)
637
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
638
        state = self.create_updated_dirstate()
 
639
        try:
 
640
            entry = state._get_entry(0, path_utf8=b'a-file')
 
641
            # No cached sha1 yet.
 
642
            self.assertEqual(b'', entry[1][0][1])
 
643
            # Set the cutoff-time into the future, so things look cacheable
 
644
            state._sha_cutoff_time()
 
645
            state._cutoff_time += 10.0
 
646
            st = os.lstat('a-file')
 
647
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
648
            self.assertEqual(b'ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
649
                             sha1sum)
 
650
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
638
651
                             state._dirblock_state)
639
652
 
640
653
            # Now, before we try to save, grab another dirstate, and take out a
661
674
        state = dirstate.DirState.on_file('dirstate')
662
675
        state.lock_read()
663
676
        try:
664
 
            entry = state._get_entry(0, path_utf8='a-file')
665
 
            self.assertEqual('', entry[1][0][1])
 
677
            entry = state._get_entry(0, path_utf8=b'a-file')
 
678
            self.assertEqual(b'', entry[1][0][1])
666
679
        finally:
667
680
            state.unlock()
668
681
 
671
684
        state = dirstate.DirState.initialize('dirstate')
672
685
        try:
673
686
            # No stat and no sha1 sum.
674
 
            state.add('a-file', 'a-file-id', 'file', None, '')
 
687
            state.add('a-file', b'a-file-id', 'file', None, b'')
675
688
            state.save()
676
689
        finally:
677
690
            state.unlock()
678
691
 
679
692
        # The dirstate should include TREE_ROOT and 'a-file' and nothing else
680
693
        expected_blocks = [
681
 
            ('', [(('', '', 'TREE_ROOT'),
682
 
                   [('d', '', 0, False, dirstate.DirState.NULLSTAT)])]),
683
 
            ('', [(('', 'a-file', 'a-file-id'),
684
 
                   [('f', '', 0, False, dirstate.DirState.NULLSTAT)])]),
 
694
            (b'', [((b'', b'', b'TREE_ROOT'),
 
695
                    [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT)])]),
 
696
            (b'', [((b'', b'a-file', b'a-file-id'),
 
697
                    [(b'f', b'', 0, False, dirstate.DirState.NULLSTAT)])]),
685
698
        ]
686
699
 
687
700
        state = dirstate.DirState.on_file('dirstate')
691
704
            self.assertEqual(expected_blocks, state._dirblocks)
692
705
 
693
706
            # Now modify the state, but mark it as inconsistent
694
 
            state.add('a-dir', 'a-dir-id', 'directory', None, '')
 
707
            state.add('a-dir', b'a-dir-id', 'directory', None, b'')
695
708
            state._changes_aborted = True
696
709
            state.save()
697
710
        finally:
710
723
 
711
724
    def test_initialize(self):
712
725
        expected_result = ([], [
713
 
            (('', '', 'TREE_ROOT'), # common details
714
 
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
715
 
             ])
 
726
            ((b'', b'', b'TREE_ROOT'),  # common details
 
727
             [(b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
728
              ])
716
729
            ])
717
730
        state = dirstate.DirState.initialize('dirstate')
718
731
        try:
723
736
        # On win32 you can't read from a locked file, even within the same
724
737
        # process. So we have to unlock and release before we check the file
725
738
        # contents.
726
 
        self.assertFileEqual(''.join(lines), 'dirstate')
727
 
        state.lock_read() # check_state_with_reopen will unlock
 
739
        self.assertFileEqual(b''.join(lines), 'dirstate')
 
740
        state.lock_read()  # check_state_with_reopen will unlock
728
741
        self.check_state_with_reopen(expected_result, state)
729
742
 
730
743
 
731
744
class TestDirStateManipulations(TestCaseWithDirState):
732
745
 
 
746
    def make_minimal_tree(self):
 
747
        tree1 = self.make_branch_and_memory_tree('tree1')
 
748
        tree1.lock_write()
 
749
        self.addCleanup(tree1.unlock)
 
750
        tree1.add('')
 
751
        revid1 = tree1.commit('foo')
 
752
        return tree1, revid1
 
753
 
 
754
    def test_update_minimal_updates_id_index(self):
 
755
        state = self.create_dirstate_with_root_and_subdir()
 
756
        self.addCleanup(state.unlock)
 
757
        id_index = state._get_id_index()
 
758
        self.assertEqual([b'a-root-value', b'subdir-id'], sorted(id_index))
 
759
        state.add('file-name', b'file-id', 'file', None, '')
 
760
        self.assertEqual([b'a-root-value', b'file-id', b'subdir-id'],
 
761
                         sorted(id_index))
 
762
        state.update_minimal((b'', b'new-name', b'file-id'), b'f',
 
763
                             path_utf8=b'new-name')
 
764
        self.assertEqual([b'a-root-value', b'file-id', b'subdir-id'],
 
765
                         sorted(id_index))
 
766
        self.assertEqual([(b'', b'new-name', b'file-id')],
 
767
                         sorted(id_index[b'file-id']))
 
768
        state._validate()
 
769
 
733
770
    def test_set_state_from_inventory_no_content_no_parents(self):
734
771
        # setting the current inventory is a slow but important api to support.
735
 
        tree1 = self.make_branch_and_memory_tree('tree1')
736
 
        tree1.lock_write()
737
 
        try:
738
 
            tree1.add('')
739
 
            revid1 = tree1.commit('foo').encode('utf8')
740
 
            root_id = tree1.get_root_id()
741
 
            inv = tree1.inventory
742
 
        finally:
743
 
            tree1.unlock()
 
772
        tree1, revid1 = self.make_minimal_tree()
 
773
        inv = tree1.root_inventory
 
774
        root_id = inv.path2id('')
744
775
        expected_result = [], [
745
 
            (('', '', root_id), [
746
 
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
 
776
            ((b'', b'', root_id), [
 
777
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT)])]
747
778
        state = dirstate.DirState.initialize('dirstate')
748
779
        try:
749
780
            state.set_state_from_inventory(inv)
758
789
            # This will unlock it
759
790
            self.check_state_with_reopen(expected_result, state)
760
791
 
 
792
    def test_set_state_from_scratch_no_parents(self):
 
793
        tree1, revid1 = self.make_minimal_tree()
 
794
        inv = tree1.root_inventory
 
795
        root_id = inv.path2id('')
 
796
        expected_result = [], [
 
797
            ((b'', b'', root_id), [
 
798
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT)])]
 
799
        state = dirstate.DirState.initialize('dirstate')
 
800
        try:
 
801
            state.set_state_from_scratch(inv, [], [])
 
802
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
803
                             state._header_state)
 
804
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
805
                             state._dirblock_state)
 
806
        except:
 
807
            state.unlock()
 
808
            raise
 
809
        else:
 
810
            # This will unlock it
 
811
            self.check_state_with_reopen(expected_result, state)
 
812
 
 
813
    def test_set_state_from_scratch_identical_parent(self):
 
814
        tree1, revid1 = self.make_minimal_tree()
 
815
        inv = tree1.root_inventory
 
816
        root_id = inv.path2id('')
 
817
        rev_tree1 = tree1.branch.repository.revision_tree(revid1)
 
818
        d_entry = (b'd', b'', 0, False, dirstate.DirState.NULLSTAT)
 
819
        parent_entry = (b'd', b'', 0, False, revid1)
 
820
        expected_result = [revid1], [
 
821
            ((b'', b'', root_id), [d_entry, parent_entry])]
 
822
        state = dirstate.DirState.initialize('dirstate')
 
823
        try:
 
824
            state.set_state_from_scratch(inv, [(revid1, rev_tree1)], [])
 
825
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
826
                             state._header_state)
 
827
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
828
                             state._dirblock_state)
 
829
        except:
 
830
            state.unlock()
 
831
            raise
 
832
        else:
 
833
            # This will unlock it
 
834
            self.check_state_with_reopen(expected_result, state)
 
835
 
761
836
    def test_set_state_from_inventory_preserves_hashcache(self):
762
837
        # https://bugs.launchpad.net/bzr/+bug/146176
763
838
        # set_state_from_inventory should preserve the stat and hash value for
765
840
 
766
841
        tree = self.make_branch_and_tree('.')
767
842
        # depends on the default format using dirstate...
768
 
        tree.lock_write()
769
 
        try:
 
843
        with tree.lock_write():
770
844
            # make a dirstate with some valid hashcache data
771
845
            # file on disk, but that's not needed for this test
772
 
            foo_contents = 'contents of foo'
 
846
            foo_contents = b'contents of foo'
773
847
            self.build_tree_contents([('foo', foo_contents)])
774
 
            tree.add('foo', 'foo-id')
 
848
            tree.add('foo', b'foo-id')
775
849
 
776
850
            foo_stat = os.stat('foo')
777
851
            foo_packed = dirstate.pack_stat(foo_stat)
780
854
 
781
855
            # should not be cached yet, because the file's too fresh
782
856
            self.assertEqual(
783
 
                (('', 'foo', 'foo-id',),
784
 
                 [('f', '', 0, False, dirstate.DirState.NULLSTAT)]),
785
 
                tree._dirstate._get_entry(0, 'foo-id'))
 
857
                ((b'', b'foo', b'foo-id',),
 
858
                 [(b'f', b'', 0, False, dirstate.DirState.NULLSTAT)]),
 
859
                tree._dirstate._get_entry(0, b'foo-id'))
786
860
            # poke in some hashcache information - it wouldn't normally be
787
861
            # stored because it's too fresh
788
862
            tree._dirstate.update_minimal(
789
 
                ('', 'foo', 'foo-id'),
790
 
                'f', False, foo_sha, foo_packed, foo_size, 'foo')
 
863
                (b'', b'foo', b'foo-id'),
 
864
                b'f', False, foo_sha, foo_packed, foo_size, b'foo')
791
865
            # now should be cached
792
866
            self.assertEqual(
793
 
                (('', 'foo', 'foo-id',),
794
 
                 [('f', foo_sha, foo_size, False, foo_packed)]),
795
 
                tree._dirstate._get_entry(0, 'foo-id'))
 
867
                ((b'', b'foo', b'foo-id',),
 
868
                 [(b'f', foo_sha, foo_size, False, foo_packed)]),
 
869
                tree._dirstate._get_entry(0, b'foo-id'))
796
870
 
797
871
            # extract the inventory, and add something to it
798
 
            inv = tree._get_inventory()
 
872
            inv = tree._get_root_inventory()
799
873
            # should see the file we poked in...
800
 
            self.assertTrue(inv.has_id('foo-id'))
 
874
            self.assertTrue(inv.has_id(b'foo-id'))
801
875
            self.assertTrue(inv.has_filename('foo'))
802
 
            inv.add_path('bar', 'file', 'bar-id')
 
876
            inv.add_path('bar', 'file', b'bar-id')
803
877
            tree._dirstate._validate()
804
878
            # this used to cause it to lose its hashcache
805
879
            tree._dirstate.set_state_from_inventory(inv)
806
880
            tree._dirstate._validate()
807
 
        finally:
808
 
            tree.unlock()
809
881
 
810
 
        tree.lock_read()
811
 
        try:
 
882
        with tree.lock_read():
812
883
            # now check that the state still has the original hashcache value
813
884
            state = tree._dirstate
814
885
            state._validate()
815
 
            foo_tuple = state._get_entry(0, path_utf8='foo')
 
886
            foo_tuple = state._get_entry(0, path_utf8=b'foo')
816
887
            self.assertEqual(
817
 
                (('', 'foo', 'foo-id',),
818
 
                 [('f', foo_sha, len(foo_contents), False,
 
888
                ((b'', b'foo', b'foo-id',),
 
889
                 [(b'f', foo_sha, len(foo_contents), False,
819
890
                   dirstate.pack_stat(foo_stat))]),
820
891
                foo_tuple)
821
 
        finally:
822
 
            tree.unlock()
823
892
 
824
893
    def test_set_state_from_inventory_mixed_paths(self):
825
894
        tree1 = self.make_branch_and_tree('tree1')
828
897
        tree1.lock_write()
829
898
        try:
830
899
            tree1.add(['a', 'a/b', 'a-b', 'a/b/foo', 'a-b/bar'],
831
 
                      ['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
832
 
            tree1.commit('rev1', rev_id='rev1')
833
 
            root_id = tree1.get_root_id()
834
 
            inv = tree1.inventory
 
900
                      [b'a-id', b'b-id', b'a-b-id', b'foo-id', b'bar-id'])
 
901
            tree1.commit('rev1', rev_id=b'rev1')
 
902
            root_id = tree1.path2id('')
 
903
            inv = tree1.root_inventory
835
904
        finally:
836
905
            tree1.unlock()
837
 
        expected_result1 = [('', '', root_id, 'd'),
838
 
                            ('', 'a', 'a-id', 'd'),
839
 
                            ('', 'a-b', 'a-b-id', 'd'),
840
 
                            ('a', 'b', 'b-id', 'd'),
841
 
                            ('a/b', 'foo', 'foo-id', 'f'),
842
 
                            ('a-b', 'bar', 'bar-id', 'f'),
843
 
                           ]
844
 
        expected_result2 = [('', '', root_id, 'd'),
845
 
                            ('', 'a', 'a-id', 'd'),
846
 
                            ('', 'a-b', 'a-b-id', 'd'),
847
 
                            ('a-b', 'bar', 'bar-id', 'f'),
848
 
                           ]
 
906
        expected_result1 = [(b'', b'', root_id, b'd'),
 
907
                            (b'', b'a', b'a-id', b'd'),
 
908
                            (b'', b'a-b', b'a-b-id', b'd'),
 
909
                            (b'a', b'b', b'b-id', b'd'),
 
910
                            (b'a/b', b'foo', b'foo-id', b'f'),
 
911
                            (b'a-b', b'bar', b'bar-id', b'f'),
 
912
                            ]
 
913
        expected_result2 = [(b'', b'', root_id, b'd'),
 
914
                            (b'', b'a', b'a-id', b'd'),
 
915
                            (b'', b'a-b', b'a-b-id', b'd'),
 
916
                            (b'a-b', b'bar', b'bar-id', b'f'),
 
917
                            ]
849
918
        state = dirstate.DirState.initialize('dirstate')
850
919
        try:
851
920
            state.set_state_from_inventory(inv)
853
922
            for entry in state._iter_entries():
854
923
                values.append(entry[0] + entry[1][0][:1])
855
924
            self.assertEqual(expected_result1, values)
856
 
            del inv['b-id']
 
925
            inv.delete(b'b-id')
857
926
            state.set_state_from_inventory(inv)
858
927
            values = []
859
928
            for entry in state._iter_entries():
867
936
        state = dirstate.DirState.initialize('dirstate')
868
937
        try:
869
938
            # check precondition to be sure the state does change appropriately.
870
 
            root_entry = (('', '', 'TREE_ROOT'), [('d', '', 0, False, 'x'*32)])
 
939
            root_entry = ((b'', b'', b'TREE_ROOT'), [
 
940
                          (b'd', b'', 0, False, b'x' * 32)])
871
941
            self.assertEqual([root_entry], list(state._iter_entries()))
872
 
            self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
 
942
            self.assertEqual(root_entry, state._get_entry(0, path_utf8=b''))
873
943
            self.assertEqual(root_entry,
874
 
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
944
                             state._get_entry(0, fileid_utf8=b'TREE_ROOT'))
875
945
            self.assertEqual((None, None),
876
 
                             state._get_entry(0, fileid_utf8='second-root-id'))
877
 
            state.set_path_id('', 'second-root-id')
878
 
            new_root_entry = (('', '', 'second-root-id'),
879
 
                              [('d', '', 0, False, 'x'*32)])
 
946
                             state._get_entry(0, fileid_utf8=b'second-root-id'))
 
947
            state.set_path_id(b'', b'second-root-id')
 
948
            new_root_entry = ((b'', b'', b'second-root-id'),
 
949
                              [(b'd', b'', 0, False, b'x' * 32)])
880
950
            expected_rows = [new_root_entry]
881
951
            self.assertEqual(expected_rows, list(state._iter_entries()))
882
 
            self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
883
 
            self.assertEqual(new_root_entry, 
884
 
                             state._get_entry(0, fileid_utf8='second-root-id'))
 
952
            self.assertEqual(
 
953
                new_root_entry, state._get_entry(0, path_utf8=b''))
 
954
            self.assertEqual(new_root_entry,
 
955
                             state._get_entry(0, fileid_utf8=b'second-root-id'))
885
956
            self.assertEqual((None, None),
886
 
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
957
                             state._get_entry(0, fileid_utf8=b'TREE_ROOT'))
887
958
            # should work across save too
888
959
            state.save()
889
960
        finally:
900
971
        """Set the root file id in a dirstate with parents"""
901
972
        mt = self.make_branch_and_tree('mt')
902
973
        # in case the default tree format uses a different root id
903
 
        mt.set_root_id('TREE_ROOT')
904
 
        mt.commit('foo', rev_id='parent-revid')
905
 
        rt = mt.branch.repository.revision_tree('parent-revid')
 
974
        mt.set_root_id(b'TREE_ROOT')
 
975
        mt.commit('foo', rev_id=b'parent-revid')
 
976
        rt = mt.branch.repository.revision_tree(b'parent-revid')
906
977
        state = dirstate.DirState.initialize('dirstate')
907
978
        state._validate()
908
979
        try:
909
 
            state.set_parent_trees([('parent-revid', rt)], ghosts=[])
910
 
            root_entry = (('', '', 'TREE_ROOT'),
911
 
                          [('d', '', 0, False, 'x'*32),
912
 
                           ('d', '', 0, False, 'parent-revid')])
913
 
            self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
 
980
            state.set_parent_trees([(b'parent-revid', rt)], ghosts=[])
 
981
            root_entry = ((b'', b'', b'TREE_ROOT'),
 
982
                          [(b'd', b'', 0, False, b'x' * 32),
 
983
                           (b'd', b'', 0, False, b'parent-revid')])
 
984
            self.assertEqual(root_entry, state._get_entry(0, path_utf8=b''))
914
985
            self.assertEqual(root_entry,
915
 
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
986
                             state._get_entry(0, fileid_utf8=b'TREE_ROOT'))
916
987
            self.assertEqual((None, None),
917
 
                             state._get_entry(0, fileid_utf8='Asecond-root-id'))
918
 
            state.set_path_id('', 'Asecond-root-id')
 
988
                             state._get_entry(0, fileid_utf8=b'Asecond-root-id'))
 
989
            state.set_path_id(b'', b'Asecond-root-id')
919
990
            state._validate()
920
991
            # now see that it is what we expected
921
 
            old_root_entry = (('', '', 'TREE_ROOT'),
922
 
                              [('a', '', 0, False, ''),
923
 
                               ('d', '', 0, False, 'parent-revid')])
924
 
            new_root_entry = (('', '', 'Asecond-root-id'),
925
 
                              [('d', '', 0, False, ''),
926
 
                               ('a', '', 0, False, '')])
 
992
            old_root_entry = ((b'', b'', b'TREE_ROOT'),
 
993
                              [(b'a', b'', 0, False, b''),
 
994
                               (b'd', b'', 0, False, b'parent-revid')])
 
995
            new_root_entry = ((b'', b'', b'Asecond-root-id'),
 
996
                              [(b'd', b'', 0, False, b''),
 
997
                               (b'a', b'', 0, False, b'')])
927
998
            expected_rows = [new_root_entry, old_root_entry]
928
999
            state._validate()
929
1000
            self.assertEqual(expected_rows, list(state._iter_entries()))
930
 
            self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
931
 
            self.assertEqual(old_root_entry, state._get_entry(1, path_utf8=''))
 
1001
            self.assertEqual(
 
1002
                new_root_entry, state._get_entry(0, path_utf8=b''))
 
1003
            self.assertEqual(
 
1004
                old_root_entry, state._get_entry(1, path_utf8=b''))
932
1005
            self.assertEqual((None, None),
933
 
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
1006
                             state._get_entry(0, fileid_utf8=b'TREE_ROOT'))
934
1007
            self.assertEqual(old_root_entry,
935
 
                             state._get_entry(1, fileid_utf8='TREE_ROOT'))
 
1008
                             state._get_entry(1, fileid_utf8=b'TREE_ROOT'))
936
1009
            self.assertEqual(new_root_entry,
937
 
                             state._get_entry(0, fileid_utf8='Asecond-root-id'))
 
1010
                             state._get_entry(0, fileid_utf8=b'Asecond-root-id'))
938
1011
            self.assertEqual((None, None),
939
 
                             state._get_entry(1, fileid_utf8='Asecond-root-id'))
 
1012
                             state._get_entry(1, fileid_utf8=b'Asecond-root-id'))
940
1013
            # should work across save too
941
1014
            state.save()
942
1015
        finally:
953
1026
        state.lock_write()
954
1027
        try:
955
1028
            state._validate()
956
 
            state.set_path_id('', 'tree-root-2')
 
1029
            state.set_path_id(b'', b'tree-root-2')
957
1030
            state._validate()
958
1031
        finally:
959
1032
            state.unlock()
967
1040
            revid1 = tree1.commit('foo')
968
1041
        finally:
969
1042
            tree1.unlock()
970
 
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
 
1043
        branch2 = tree1.branch.controldir.clone('tree2').open_branch()
971
1044
        tree2 = memorytree.MemoryTree.create_on_branch(branch2)
972
1045
        tree2.lock_write()
973
1046
        try:
974
1047
            revid2 = tree2.commit('foo')
975
 
            root_id = tree2.get_root_id()
 
1048
            root_id = tree2.path2id('')
976
1049
        finally:
977
1050
            tree2.unlock()
978
1051
        state = dirstate.DirState.initialize('dirstate')
979
1052
        try:
980
 
            state.set_path_id('', root_id)
 
1053
            state.set_path_id(b'', root_id)
981
1054
            state.set_parent_trees(
982
1055
                ((revid1, tree1.branch.repository.revision_tree(revid1)),
983
1056
                 (revid2, tree2.branch.repository.revision_tree(revid2)),
984
 
                 ('ghost-rev', None)),
985
 
                ['ghost-rev'])
 
1057
                 (b'ghost-rev', None)),
 
1058
                [b'ghost-rev'])
986
1059
            # check we can reopen and use the dirstate after setting parent
987
1060
            # trees.
988
1061
            state._validate()
993
1066
        state = dirstate.DirState.on_file('dirstate')
994
1067
        state.lock_write()
995
1068
        try:
996
 
            self.assertEqual([revid1, revid2, 'ghost-rev'],
 
1069
            self.assertEqual([revid1, revid2, b'ghost-rev'],
997
1070
                             state.get_parent_ids())
998
1071
            # iterating the entire state ensures that the state is parsable.
999
1072
            list(state._iter_entries())
1000
1073
            # be sure that it sets not appends - change it
1001
1074
            state.set_parent_trees(
1002
1075
                ((revid1, tree1.branch.repository.revision_tree(revid1)),
1003
 
                 ('ghost-rev', None)),
1004
 
                ['ghost-rev'])
 
1076
                 (b'ghost-rev', None)),
 
1077
                [b'ghost-rev'])
1005
1078
            # and now put it back.
1006
1079
            state.set_parent_trees(
1007
1080
                ((revid1, tree1.branch.repository.revision_tree(revid1)),
1008
1081
                 (revid2, tree2.branch.repository.revision_tree(revid2)),
1009
 
                 ('ghost-rev', tree2.branch.repository.revision_tree(
1010
 
                                   _mod_revision.NULL_REVISION))),
1011
 
                ['ghost-rev'])
1012
 
            self.assertEqual([revid1, revid2, 'ghost-rev'],
 
1082
                 (b'ghost-rev', tree2.branch.repository.revision_tree(
 
1083
                     _mod_revision.NULL_REVISION))),
 
1084
                [b'ghost-rev'])
 
1085
            self.assertEqual([revid1, revid2, b'ghost-rev'],
1013
1086
                             state.get_parent_ids())
1014
1087
            # the ghost should be recorded as such by set_parent_trees.
1015
 
            self.assertEqual(['ghost-rev'], state.get_ghosts())
 
1088
            self.assertEqual([b'ghost-rev'], state.get_ghosts())
1016
1089
            self.assertEqual(
1017
 
                [(('', '', root_id), [
1018
 
                  ('d', '', 0, False, dirstate.DirState.NULLSTAT),
1019
 
                  ('d', '', 0, False, revid1),
1020
 
                  ('d', '', 0, False, revid1)
 
1090
                [((b'', b'', root_id), [
 
1091
                  (b'd', b'', 0, False, dirstate.DirState.NULLSTAT),
 
1092
                  (b'd', b'', 0, False, revid1),
 
1093
                  (b'd', b'', 0, False, revid1)
1021
1094
                  ])],
1022
1095
                list(state._iter_entries()))
1023
1096
        finally:
1032
1105
        tree1.lock_write()
1033
1106
        try:
1034
1107
            tree1.add('')
1035
 
            tree1.add(['a file'], ['file-id'], ['file'])
1036
 
            tree1.put_file_bytes_non_atomic('file-id', 'file-content')
 
1108
            tree1.add(['a file'], [b'file-id'], ['file'])
 
1109
            tree1.put_file_bytes_non_atomic('a file', b'file-content')
1037
1110
            revid1 = tree1.commit('foo')
1038
1111
        finally:
1039
1112
            tree1.unlock()
1040
 
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
 
1113
        branch2 = tree1.branch.controldir.clone('tree2').open_branch()
1041
1114
        tree2 = memorytree.MemoryTree.create_on_branch(branch2)
1042
1115
        tree2.lock_write()
1043
1116
        try:
1044
 
            tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
 
1117
            tree2.put_file_bytes_non_atomic('a file', b'new file-content')
1045
1118
            revid2 = tree2.commit('foo')
1046
 
            root_id = tree2.get_root_id()
 
1119
            root_id = tree2.path2id('')
1047
1120
        finally:
1048
1121
            tree2.unlock()
1049
1122
        # check the layout in memory
1050
 
        expected_result = [revid1.encode('utf8'), revid2.encode('utf8')], [
1051
 
            (('', '', root_id), [
1052
 
             ('d', '', 0, False, dirstate.DirState.NULLSTAT),
1053
 
             ('d', '', 0, False, revid1.encode('utf8')),
1054
 
             ('d', '', 0, False, revid1.encode('utf8'))
 
1123
        expected_result = [revid1, revid2], [
 
1124
            ((b'', b'', root_id), [
 
1125
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT),
 
1126
             (b'd', b'', 0, False, revid1),
 
1127
             (b'd', b'', 0, False, revid1)
1055
1128
             ]),
1056
 
            (('', 'a file', 'file-id'), [
1057
 
             ('a', '', 0, False, ''),
1058
 
             ('f', '2439573625385400f2a669657a7db6ae7515d371', 12, False,
1059
 
              revid1.encode('utf8')),
1060
 
             ('f', '542e57dc1cda4af37cb8e55ec07ce60364bb3c7d', 16, False,
1061
 
              revid2.encode('utf8'))
 
1129
            ((b'', b'a file', b'file-id'), [
 
1130
             (b'a', b'', 0, False, b''),
 
1131
             (b'f', b'2439573625385400f2a669657a7db6ae7515d371', 12, False,
 
1132
              revid1),
 
1133
             (b'f', b'542e57dc1cda4af37cb8e55ec07ce60364bb3c7d', 16, False,
 
1134
              revid2)
1062
1135
             ])
1063
1136
            ]
1064
1137
        state = dirstate.DirState.initialize('dirstate')
1065
1138
        try:
1066
 
            state.set_path_id('', root_id)
 
1139
            state.set_path_id(b'', root_id)
1067
1140
            state.set_parent_trees(
1068
1141
                ((revid1, tree1.branch.repository.revision_tree(revid1)),
1069
1142
                 (revid2, tree2.branch.repository.revision_tree(revid2)),
1075
1148
            # check_state_with_reopen will unlock
1076
1149
            self.check_state_with_reopen(expected_result, state)
1077
1150
 
1078
 
    ### add a path via _set_data - so we dont need delta work, just
 
1151
    # add a path via _set_data - so we dont need delta work, just
1079
1152
    # raw data in, and ensure that it comes out via get_lines happily.
1080
1153
 
1081
1154
    def test_add_path_to_root_no_parents_all_data(self):
1086
1159
        # the 1*20 is the sha1 pretend value.
1087
1160
        state = dirstate.DirState.initialize('dirstate')
1088
1161
        expected_entries = [
1089
 
            (('', '', 'TREE_ROOT'), [
1090
 
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
 
1162
            ((b'', b'', b'TREE_ROOT'), [
 
1163
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
1091
1164
             ]),
1092
 
            (('', 'a file', 'a-file-id'), [
1093
 
             ('f', '1'*20, 19, False, dirstate.pack_stat(stat)), # current tree
 
1165
            ((b'', b'a file', b'a-file-id'), [
 
1166
             (b'f', b'1' * 20, 19, False, dirstate.pack_stat(stat)),  # current tree
1094
1167
             ]),
1095
1168
            ]
1096
1169
        try:
1097
 
            state.add('a file', 'a-file-id', 'file', stat, '1'*20)
 
1170
            state.add('a file', b'a-file-id', 'file', stat, b'1' * 20)
1098
1171
            # having added it, it should be in the output of iter_entries.
1099
1172
            self.assertEqual(expected_entries, list(state._iter_entries()))
1100
1173
            # saving and reloading should not affect this.
1117
1190
        state = dirstate.DirState.initialize('dirstate')
1118
1191
        self.addCleanup(state.unlock)
1119
1192
        self.assertRaises(errors.NotVersionedError, state.add,
1120
 
                          'unversioned/a file', 'a-file-id', 'file', None, None)
 
1193
                          'unversioned/a file', b'a-file-id', 'file', None, None)
1121
1194
 
1122
1195
    def test_add_directory_to_root_no_parents_all_data(self):
1123
1196
        # The most trivial addition of a dir is when there are no parents and
1125
1198
        self.build_tree(['a dir/'])
1126
1199
        stat = os.lstat('a dir')
1127
1200
        expected_entries = [
1128
 
            (('', '', 'TREE_ROOT'), [
1129
 
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
 
1201
            ((b'', b'', b'TREE_ROOT'), [
 
1202
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
1130
1203
             ]),
1131
 
            (('', 'a dir', 'a dir id'), [
1132
 
             ('d', '', 0, False, dirstate.pack_stat(stat)), # current tree
 
1204
            ((b'', b'a dir', b'a dir id'), [
 
1205
             (b'd', b'', 0, False, dirstate.pack_stat(stat)),  # current tree
1133
1206
             ]),
1134
1207
            ]
1135
1208
        state = dirstate.DirState.initialize('dirstate')
1136
1209
        try:
1137
 
            state.add('a dir', 'a dir id', 'directory', stat, None)
 
1210
            state.add('a dir', b'a dir id', 'directory', stat, None)
1138
1211
            # having added it, it should be in the output of iter_entries.
1139
1212
            self.assertEqual(expected_entries, list(state._iter_entries()))
1140
1213
            # saving and reloading should not affect this.
1151
1224
        # The most trivial addition of a symlink when there are no parents and
1152
1225
        # its in the root and all data about the file is supplied
1153
1226
        # bzr doesn't support fake symlinks on windows, yet.
1154
 
        self.requireFeature(tests.SymlinkFeature)
 
1227
        self.requireFeature(features.SymlinkFeature)
1155
1228
        os.symlink(target, link_name)
1156
1229
        stat = os.lstat(link_name)
1157
1230
        expected_entries = [
1158
 
            (('', '', 'TREE_ROOT'), [
1159
 
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
 
1231
            ((b'', b'', b'TREE_ROOT'), [
 
1232
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
1160
1233
             ]),
1161
 
            (('', link_name.encode('UTF-8'), 'a link id'), [
1162
 
             ('l', target.encode('UTF-8'), stat[6],
1163
 
              False, dirstate.pack_stat(stat)), # current tree
 
1234
            ((b'', link_name.encode('UTF-8'), b'a link id'), [
 
1235
             (b'l', target.encode('UTF-8'), stat[6],
 
1236
              False, dirstate.pack_stat(stat)),  # current tree
1164
1237
             ]),
1165
1238
            ]
1166
1239
        state = dirstate.DirState.initialize('dirstate')
1167
1240
        try:
1168
 
            state.add(link_name, 'a link id', 'symlink', stat,
 
1241
            state.add(link_name, b'a link id', 'symlink', stat,
1169
1242
                      target.encode('UTF-8'))
1170
1243
            # having added it, it should be in the output of iter_entries.
1171
1244
            self.assertEqual(expected_entries, list(state._iter_entries()))
1179
1252
        self.assertEqual(expected_entries, list(state._iter_entries()))
1180
1253
 
1181
1254
    def test_add_symlink_to_root_no_parents_all_data(self):
1182
 
        self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
 
1255
        self._test_add_symlink_to_root_no_parents_all_data(
 
1256
            u'a link', u'target')
1183
1257
 
1184
1258
    def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1185
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
1259
        self.requireFeature(features.UnicodeFilenameFeature)
1186
1260
        self._test_add_symlink_to_root_no_parents_all_data(
1187
1261
            u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1188
1262
 
1192
1266
        dirstat = os.lstat('a dir')
1193
1267
        filestat = os.lstat('a dir/a file')
1194
1268
        expected_entries = [
1195
 
            (('', '', 'TREE_ROOT'), [
1196
 
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
1197
 
             ]),
1198
 
            (('', 'a dir', 'a dir id'), [
1199
 
             ('d', '', 0, False, dirstate.pack_stat(dirstat)), # current tree
1200
 
             ]),
1201
 
            (('a dir', 'a file', 'a-file-id'), [
1202
 
             ('f', '1'*20, 25, False,
1203
 
              dirstate.pack_stat(filestat)), # current tree details
 
1269
            ((b'', b'', b'TREE_ROOT'), [
 
1270
             (b'd', b'', 0, False, dirstate.DirState.NULLSTAT),  # current tree
 
1271
             ]),
 
1272
            ((b'', b'a dir', b'a dir id'), [
 
1273
             (b'd', b'', 0, False, dirstate.pack_stat(dirstat)),  # current tree
 
1274
             ]),
 
1275
            ((b'a dir', b'a file', b'a-file-id'), [
 
1276
             (b'f', b'1' * 20, 25, False,
 
1277
              dirstate.pack_stat(filestat)),  # current tree details
1204
1278
             ]),
1205
1279
            ]
1206
1280
        state = dirstate.DirState.initialize('dirstate')
1207
1281
        try:
1208
 
            state.add('a dir', 'a dir id', 'directory', dirstat, None)
1209
 
            state.add('a dir/a file', 'a-file-id', 'file', filestat, '1'*20)
 
1282
            state.add('a dir', b'a dir id', 'directory', dirstat, None)
 
1283
            state.add('a dir/a file', b'a-file-id',
 
1284
                      'file', filestat, b'1' * 20)
1210
1285
            # added it, it should be in the output of iter_entries.
1211
1286
            self.assertEqual(expected_entries, list(state._iter_entries()))
1212
1287
            # saving and reloading should not affect this.
1222
1297
        # make a dirstate and add a tree reference
1223
1298
        state = dirstate.DirState.initialize('dirstate')
1224
1299
        expected_entry = (
1225
 
            ('', 'subdir', 'subdir-id'),
1226
 
            [('t', 'subtree-123123', 0, False,
1227
 
              'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')],
 
1300
            (b'', b'subdir', b'subdir-id'),
 
1301
            [(b't', b'subtree-123123', 0, False,
 
1302
              b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')],
1228
1303
            )
1229
1304
        try:
1230
 
            state.add('subdir', 'subdir-id', 'tree-reference', None, 'subtree-123123')
1231
 
            entry = state._get_entry(0, 'subdir-id', 'subdir')
 
1305
            state.add('subdir', b'subdir-id', 'tree-reference',
 
1306
                      None, b'subtree-123123')
 
1307
            entry = state._get_entry(0, b'subdir-id', b'subdir')
1232
1308
            self.assertEqual(entry, expected_entry)
1233
1309
            state._validate()
1234
1310
            state.save()
1238
1314
        state.lock_read()
1239
1315
        self.addCleanup(state.unlock)
1240
1316
        state._validate()
1241
 
        entry2 = state._get_entry(0, 'subdir-id', 'subdir')
 
1317
        entry2 = state._get_entry(0, b'subdir-id', b'subdir')
1242
1318
        self.assertEqual(entry, entry2)
1243
1319
        self.assertEqual(entry, expected_entry)
1244
1320
        # and lookup by id should work too
1245
 
        entry2 = state._get_entry(0, fileid_utf8='subdir-id')
 
1321
        entry2 = state._get_entry(0, fileid_utf8=b'subdir-id')
1246
1322
        self.assertEqual(entry, expected_entry)
1247
1323
 
1248
1324
    def test_add_forbidden_names(self):
1249
1325
        state = dirstate.DirState.initialize('dirstate')
1250
1326
        self.addCleanup(state.unlock)
1251
1327
        self.assertRaises(errors.BzrError,
1252
 
            state.add, '.', 'ass-id', 'directory', None, None)
 
1328
                          state.add, '.', b'ass-id', 'directory', None, None)
1253
1329
        self.assertRaises(errors.BzrError,
1254
 
            state.add, '..', 'ass-id', 'directory', None, None)
 
1330
                          state.add, '..', b'ass-id', 'directory', None, None)
1255
1331
 
1256
1332
    def test_set_state_with_rename_b_a_bug_395556(self):
1257
1333
        # bug 395556 uncovered a bug where the dirstate ends up with a false
1261
1337
        # incorrect absent in tree 1, and future changes go to pot.
1262
1338
        tree1 = self.make_branch_and_tree('tree1')
1263
1339
        self.build_tree(['tree1/b'])
1264
 
        tree1.lock_write()
1265
 
        try:
1266
 
            tree1.add(['b'], ['b-id'])
1267
 
            root_id = tree1.get_root_id()
1268
 
            inv = tree1.inventory
 
1340
        with tree1.lock_write():
 
1341
            tree1.add(['b'], [b'b-id'])
 
1342
            root_id = tree1.path2id('')
 
1343
            inv = tree1.root_inventory
1269
1344
            state = dirstate.DirState.initialize('dirstate')
1270
1345
            try:
1271
1346
                # Set the initial state with 'b'
1272
1347
                state.set_state_from_inventory(inv)
1273
 
                inv.rename('b-id', root_id, 'a')
 
1348
                inv.rename(b'b-id', root_id, 'a')
1274
1349
                # Set the new state with 'a', which currently corrupts.
1275
1350
                state.set_state_from_inventory(inv)
1276
 
                expected_result1 = [('', '', root_id, 'd'),
1277
 
                                    ('', 'a', 'b-id', 'f'),
1278
 
                                   ]
 
1351
                expected_result1 = [(b'', b'', root_id, b'd'),
 
1352
                                    (b'', b'a', b'b-id', b'f'),
 
1353
                                    ]
1279
1354
                values = []
1280
1355
                for entry in state._iter_entries():
1281
1356
                    values.append(entry[0] + entry[1][0][:1])
1282
1357
                self.assertEqual(expected_result1, values)
1283
1358
            finally:
1284
1359
                state.unlock()
1285
 
        finally:
1286
 
            tree1.unlock()
 
1360
 
 
1361
 
 
1362
class TestDirStateHashUpdates(TestCaseWithDirState):
 
1363
 
 
1364
    def do_update_entry(self, state, path):
 
1365
        entry = state._get_entry(0, path_utf8=path)
 
1366
        stat = os.lstat(path)
 
1367
        return dirstate.update_entry(state, entry, os.path.abspath(path), stat)
 
1368
 
 
1369
    def _read_state_content(self, state):
 
1370
        """Read the content of the dirstate file.
 
1371
 
 
1372
        On Windows when one process locks a file, you can't even open() the
 
1373
        file in another process (to read it). So we go directly to
 
1374
        state._state_file. This should always be the exact disk representation,
 
1375
        so it is reasonable to do so.
 
1376
        DirState also always seeks before reading, so it doesn't matter if we
 
1377
        bump the file pointer.
 
1378
        """
 
1379
        state._state_file.seek(0)
 
1380
        return state._state_file.read()
 
1381
 
 
1382
    def test_worth_saving_limit_avoids_writing(self):
 
1383
        tree = self.make_branch_and_tree('.')
 
1384
        self.build_tree(['c', 'd'])
 
1385
        tree.lock_write()
 
1386
        tree.add(['c', 'd'], [b'c-id', b'd-id'])
 
1387
        tree.commit('add c and d')
 
1388
        state = InstrumentedDirState.on_file(tree.current_dirstate()._filename,
 
1389
                                             worth_saving_limit=2)
 
1390
        tree.unlock()
 
1391
        state.lock_write()
 
1392
        self.addCleanup(state.unlock)
 
1393
        state._read_dirblocks_if_needed()
 
1394
        state.adjust_time(+20)  # Allow things to be cached
 
1395
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1396
                         state._dirblock_state)
 
1397
        content = self._read_state_content(state)
 
1398
        self.do_update_entry(state, b'c')
 
1399
        self.assertEqual(1, len(state._known_hash_changes))
 
1400
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1401
                         state._dirblock_state)
 
1402
        state.save()
 
1403
        # It should not have set the state to IN_MEMORY_UNMODIFIED because the
 
1404
        # hash values haven't been written out.
 
1405
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1406
                         state._dirblock_state)
 
1407
        self.assertEqual(content, self._read_state_content(state))
 
1408
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1409
                         state._dirblock_state)
 
1410
        self.do_update_entry(state, b'd')
 
1411
        self.assertEqual(2, len(state._known_hash_changes))
 
1412
        state.save()
 
1413
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1414
                         state._dirblock_state)
 
1415
        self.assertEqual(0, len(state._known_hash_changes))
1287
1416
 
1288
1417
 
1289
1418
class TestGetLines(TestCaseWithDirState):
1291
1420
    def test_get_line_with_2_rows(self):
1292
1421
        state = self.create_dirstate_with_root_and_subdir()
1293
1422
        try:
1294
 
            self.assertEqual(['#bazaar dirstate flat format 3\n',
1295
 
                'crc32: 41262208\n',
1296
 
                'num_entries: 2\n',
1297
 
                '0\x00\n\x00'
1298
 
                '0\x00\n\x00'
1299
 
                '\x00\x00a-root-value\x00'
1300
 
                'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
1301
 
                '\x00subdir\x00subdir-id\x00'
1302
 
                'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
1303
 
                ], state.get_lines())
 
1423
            self.assertEqual([b'#bazaar dirstate flat format 3\n',
 
1424
                              b'crc32: 41262208\n',
 
1425
                              b'num_entries: 2\n',
 
1426
                              b'0\x00\n\x00'
 
1427
                              b'0\x00\n\x00'
 
1428
                              b'\x00\x00a-root-value\x00'
 
1429
                              b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
 
1430
                              b'\x00subdir\x00subdir-id\x00'
 
1431
                              b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
 
1432
                              ], state.get_lines())
1304
1433
        finally:
1305
1434
            state.unlock()
1306
1435
 
1308
1437
        state = self.create_dirstate_with_root()
1309
1438
        try:
1310
1439
            self.assertEqual(
1311
 
                '\x00\x00a-root-value\x00d\x00\x000\x00n'
1312
 
                '\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk',
 
1440
                b'\x00\x00a-root-value\x00d\x00\x000\x00n'
 
1441
                b'\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk',
1313
1442
                state._entry_to_line(state._dirblocks[0][1][0]))
1314
1443
        finally:
1315
1444
            state.unlock()
1316
1445
 
1317
1446
    def test_entry_to_line_with_parent(self):
1318
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1319
 
        root_entry = ('', '', 'a-root-value'), [
1320
 
            ('d', '', 0, False, packed_stat), # current tree details
1321
 
             # first: a pointer to the current location
1322
 
            ('a', 'dirname/basename', 0, False, ''),
 
1447
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1448
        root_entry = (b'', b'', b'a-root-value'), [
 
1449
            (b'd', b'', 0, False, packed_stat),  # current tree details
 
1450
            # first: a pointer to the current location
 
1451
            (b'a', b'dirname/basename', 0, False, b''),
1323
1452
            ]
1324
1453
        state = dirstate.DirState.initialize('dirstate')
1325
1454
        try:
1326
1455
            self.assertEqual(
1327
 
                '\x00\x00a-root-value\x00'
1328
 
                'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
1329
 
                'a\x00dirname/basename\x000\x00n\x00',
 
1456
                b'\x00\x00a-root-value\x00'
 
1457
                b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
1458
                b'a\x00dirname/basename\x000\x00n\x00',
1330
1459
                state._entry_to_line(root_entry))
1331
1460
        finally:
1332
1461
            state.unlock()
1333
1462
 
1334
1463
    def test_entry_to_line_with_two_parents_at_different_paths(self):
1335
1464
        # / in the tree, at / in one parent and /dirname/basename in the other.
1336
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1337
 
        root_entry = ('', '', 'a-root-value'), [
1338
 
            ('d', '', 0, False, packed_stat), # current tree details
1339
 
            ('d', '', 0, False, 'rev_id'), # first parent details
1340
 
             # second: a pointer to the current location
1341
 
            ('a', 'dirname/basename', 0, False, ''),
 
1465
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1466
        root_entry = (b'', b'', b'a-root-value'), [
 
1467
            (b'd', b'', 0, False, packed_stat),  # current tree details
 
1468
            (b'd', b'', 0, False, b'rev_id'),  # first parent details
 
1469
            # second: a pointer to the current location
 
1470
            (b'a', b'dirname/basename', 0, False, b''),
1342
1471
            ]
1343
1472
        state = dirstate.DirState.initialize('dirstate')
1344
1473
        try:
1345
1474
            self.assertEqual(
1346
 
                '\x00\x00a-root-value\x00'
1347
 
                'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
1348
 
                'd\x00\x000\x00n\x00rev_id\x00'
1349
 
                'a\x00dirname/basename\x000\x00n\x00',
 
1475
                b'\x00\x00a-root-value\x00'
 
1476
                b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
1477
                b'd\x00\x000\x00n\x00rev_id\x00'
 
1478
                b'a\x00dirname/basename\x000\x00n\x00',
1350
1479
                state._entry_to_line(root_entry))
1351
1480
        finally:
1352
1481
            state.unlock()
1354
1483
    def test_iter_entries(self):
1355
1484
        # we should be able to iterate the dirstate entries from end to end
1356
1485
        # this is for get_lines to be easy to read.
1357
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1486
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1358
1487
        dirblocks = []
1359
 
        root_entries = [(('', '', 'a-root-value'), [
1360
 
            ('d', '', 0, False, packed_stat), # current tree details
 
1488
        root_entries = [((b'', b'', b'a-root-value'), [
 
1489
            (b'd', b'', 0, False, packed_stat),  # current tree details
1361
1490
            ])]
1362
1491
        dirblocks.append(('', root_entries))
1363
1492
        # add two files in the root
1364
 
        subdir_entry = ('', 'subdir', 'subdir-id'), [
1365
 
            ('d', '', 0, False, packed_stat), # current tree details
 
1493
        subdir_entry = (b'', b'subdir', b'subdir-id'), [
 
1494
            (b'd', b'', 0, False, packed_stat),  # current tree details
1366
1495
            ]
1367
 
        afile_entry = ('', 'afile', 'afile-id'), [
1368
 
            ('f', 'sha1value', 34, False, packed_stat), # current tree details
 
1496
        afile_entry = (b'', b'afile', b'afile-id'), [
 
1497
            (b'f', b'sha1value', 34, False, packed_stat),  # current tree details
1369
1498
            ]
1370
1499
        dirblocks.append(('', [subdir_entry, afile_entry]))
1371
1500
        # and one in subdir
1372
 
        file_entry2 = ('subdir', '2file', '2file-id'), [
1373
 
            ('f', 'sha1value', 23, False, packed_stat), # current tree details
 
1501
        file_entry2 = (b'subdir', b'2file', b'2file-id'), [
 
1502
            (b'f', b'sha1value', 23, False, packed_stat),  # current tree details
1374
1503
            ]
1375
1504
        dirblocks.append(('subdir', [file_entry2]))
1376
1505
        state = dirstate.DirState.initialize('dirstate')
1386
1515
class TestGetBlockRowIndex(TestCaseWithDirState):
1387
1516
 
1388
1517
    def assertBlockRowIndexEqual(self, block_index, row_index, dir_present,
1389
 
        file_present, state, dirname, basename, tree_index):
 
1518
                                 file_present, state, dirname, basename, tree_index):
1390
1519
        self.assertEqual((block_index, row_index, dir_present, file_present),
1391
 
            state._get_block_entry_index(dirname, basename, tree_index))
 
1520
                         state._get_block_entry_index(dirname, basename, tree_index))
1392
1521
        if dir_present:
1393
1522
            block = state._dirblocks[block_index]
1394
1523
            self.assertEqual(dirname, block[0])
1400
1529
    def test_simple_structure(self):
1401
1530
        state = self.create_dirstate_with_root_and_subdir()
1402
1531
        self.addCleanup(state.unlock)
1403
 
        self.assertBlockRowIndexEqual(1, 0, True, True, state, '', 'subdir', 0)
1404
 
        self.assertBlockRowIndexEqual(1, 0, True, False, state, '', 'bdir', 0)
1405
 
        self.assertBlockRowIndexEqual(1, 1, True, False, state, '', 'zdir', 0)
1406
 
        self.assertBlockRowIndexEqual(2, 0, False, False, state, 'a', 'foo', 0)
 
1532
        self.assertBlockRowIndexEqual(
 
1533
            1, 0, True, True, state, b'', b'subdir', 0)
 
1534
        self.assertBlockRowIndexEqual(
 
1535
            1, 0, True, False, state, b'', b'bdir', 0)
 
1536
        self.assertBlockRowIndexEqual(
 
1537
            1, 1, True, False, state, b'', b'zdir', 0)
 
1538
        self.assertBlockRowIndexEqual(
 
1539
            2, 0, False, False, state, b'a', b'foo', 0)
1407
1540
        self.assertBlockRowIndexEqual(2, 0, False, False, state,
1408
 
                                      'subdir', 'foo', 0)
 
1541
                                      b'subdir', b'foo', 0)
1409
1542
 
1410
1543
    def test_complex_structure_exists(self):
1411
1544
        state = self.create_complex_dirstate()
1412
1545
        self.addCleanup(state.unlock)
1413
1546
        # Make sure we can find everything that exists
1414
 
        self.assertBlockRowIndexEqual(0, 0, True, True, state, '', '', 0)
1415
 
        self.assertBlockRowIndexEqual(1, 0, True, True, state, '', 'a', 0)
1416
 
        self.assertBlockRowIndexEqual(1, 1, True, True, state, '', 'b', 0)
1417
 
        self.assertBlockRowIndexEqual(1, 2, True, True, state, '', 'c', 0)
1418
 
        self.assertBlockRowIndexEqual(1, 3, True, True, state, '', 'd', 0)
1419
 
        self.assertBlockRowIndexEqual(2, 0, True, True, state, 'a', 'e', 0)
1420
 
        self.assertBlockRowIndexEqual(2, 1, True, True, state, 'a', 'f', 0)
1421
 
        self.assertBlockRowIndexEqual(3, 0, True, True, state, 'b', 'g', 0)
 
1547
        self.assertBlockRowIndexEqual(0, 0, True, True, state, b'', b'', 0)
 
1548
        self.assertBlockRowIndexEqual(1, 0, True, True, state, b'', b'a', 0)
 
1549
        self.assertBlockRowIndexEqual(1, 1, True, True, state, b'', b'b', 0)
 
1550
        self.assertBlockRowIndexEqual(1, 2, True, True, state, b'', b'c', 0)
 
1551
        self.assertBlockRowIndexEqual(1, 3, True, True, state, b'', b'd', 0)
 
1552
        self.assertBlockRowIndexEqual(2, 0, True, True, state, b'a', b'e', 0)
 
1553
        self.assertBlockRowIndexEqual(2, 1, True, True, state, b'a', b'f', 0)
 
1554
        self.assertBlockRowIndexEqual(3, 0, True, True, state, b'b', b'g', 0)
1422
1555
        self.assertBlockRowIndexEqual(3, 1, True, True, state,
1423
 
                                      'b', 'h\xc3\xa5', 0)
 
1556
                                      b'b', b'h\xc3\xa5', 0)
1424
1557
 
1425
1558
    def test_complex_structure_missing(self):
1426
1559
        state = self.create_complex_dirstate()
1427
1560
        self.addCleanup(state.unlock)
1428
1561
        # Make sure things would be inserted in the right locations
1429
1562
        # '_' comes before 'a'
1430
 
        self.assertBlockRowIndexEqual(0, 0, True, True, state, '', '', 0)
1431
 
        self.assertBlockRowIndexEqual(1, 0, True, False, state, '', '_', 0)
1432
 
        self.assertBlockRowIndexEqual(1, 1, True, False, state, '', 'aa', 0)
 
1563
        self.assertBlockRowIndexEqual(0, 0, True, True, state, b'', b'', 0)
 
1564
        self.assertBlockRowIndexEqual(1, 0, True, False, state, b'', b'_', 0)
 
1565
        self.assertBlockRowIndexEqual(1, 1, True, False, state, b'', b'aa', 0)
1433
1566
        self.assertBlockRowIndexEqual(1, 4, True, False, state,
1434
 
                                      '', 'h\xc3\xa5', 0)
1435
 
        self.assertBlockRowIndexEqual(2, 0, False, False, state, '_', 'a', 0)
1436
 
        self.assertBlockRowIndexEqual(3, 0, False, False, state, 'aa', 'a', 0)
1437
 
        self.assertBlockRowIndexEqual(4, 0, False, False, state, 'bb', 'a', 0)
 
1567
                                      b'', b'h\xc3\xa5', 0)
 
1568
        self.assertBlockRowIndexEqual(2, 0, False, False, state, b'_', b'a', 0)
 
1569
        self.assertBlockRowIndexEqual(
 
1570
            3, 0, False, False, state, b'aa', b'a', 0)
 
1571
        self.assertBlockRowIndexEqual(
 
1572
            4, 0, False, False, state, b'bb', b'a', 0)
1438
1573
        # This would be inserted between a/ and b/
1439
 
        self.assertBlockRowIndexEqual(3, 0, False, False, state, 'a/e', 'a', 0)
 
1574
        self.assertBlockRowIndexEqual(
 
1575
            3, 0, False, False, state, b'a/e', b'a', 0)
1440
1576
        # Put at the end
1441
 
        self.assertBlockRowIndexEqual(4, 0, False, False, state, 'e', 'a', 0)
 
1577
        self.assertBlockRowIndexEqual(4, 0, False, False, state, b'e', b'a', 0)
1442
1578
 
1443
1579
 
1444
1580
class TestGetEntry(TestCaseWithDirState):
1455
1591
    def test_simple_structure(self):
1456
1592
        state = self.create_dirstate_with_root_and_subdir()
1457
1593
        self.addCleanup(state.unlock)
1458
 
        self.assertEntryEqual('', '', 'a-root-value', state, '', 0)
1459
 
        self.assertEntryEqual('', 'subdir', 'subdir-id', state, 'subdir', 0)
1460
 
        self.assertEntryEqual(None, None, None, state, 'missing', 0)
1461
 
        self.assertEntryEqual(None, None, None, state, 'missing/foo', 0)
1462
 
        self.assertEntryEqual(None, None, None, state, 'subdir/foo', 0)
 
1594
        self.assertEntryEqual(b'', b'', b'a-root-value', state, b'', 0)
 
1595
        self.assertEntryEqual(
 
1596
            b'', b'subdir', b'subdir-id', state, b'subdir', 0)
 
1597
        self.assertEntryEqual(None, None, None, state, b'missing', 0)
 
1598
        self.assertEntryEqual(None, None, None, state, b'missing/foo', 0)
 
1599
        self.assertEntryEqual(None, None, None, state, b'subdir/foo', 0)
1463
1600
 
1464
1601
    def test_complex_structure_exists(self):
1465
1602
        state = self.create_complex_dirstate()
1466
1603
        self.addCleanup(state.unlock)
1467
 
        self.assertEntryEqual('', '', 'a-root-value', state, '', 0)
1468
 
        self.assertEntryEqual('', 'a', 'a-dir', state, 'a', 0)
1469
 
        self.assertEntryEqual('', 'b', 'b-dir', state, 'b', 0)
1470
 
        self.assertEntryEqual('', 'c', 'c-file', state, 'c', 0)
1471
 
        self.assertEntryEqual('', 'd', 'd-file', state, 'd', 0)
1472
 
        self.assertEntryEqual('a', 'e', 'e-dir', state, 'a/e', 0)
1473
 
        self.assertEntryEqual('a', 'f', 'f-file', state, 'a/f', 0)
1474
 
        self.assertEntryEqual('b', 'g', 'g-file', state, 'b/g', 0)
1475
 
        self.assertEntryEqual('b', 'h\xc3\xa5', 'h-\xc3\xa5-file', state,
1476
 
                              'b/h\xc3\xa5', 0)
 
1604
        self.assertEntryEqual(b'', b'', b'a-root-value', state, b'', 0)
 
1605
        self.assertEntryEqual(b'', b'a', b'a-dir', state, b'a', 0)
 
1606
        self.assertEntryEqual(b'', b'b', b'b-dir', state, b'b', 0)
 
1607
        self.assertEntryEqual(b'', b'c', b'c-file', state, b'c', 0)
 
1608
        self.assertEntryEqual(b'', b'd', b'd-file', state, b'd', 0)
 
1609
        self.assertEntryEqual(b'a', b'e', b'e-dir', state, b'a/e', 0)
 
1610
        self.assertEntryEqual(b'a', b'f', b'f-file', state, b'a/f', 0)
 
1611
        self.assertEntryEqual(b'b', b'g', b'g-file', state, b'b/g', 0)
 
1612
        self.assertEntryEqual(b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file', state,
 
1613
                              b'b/h\xc3\xa5', 0)
1477
1614
 
1478
1615
    def test_complex_structure_missing(self):
1479
1616
        state = self.create_complex_dirstate()
1480
1617
        self.addCleanup(state.unlock)
1481
 
        self.assertEntryEqual(None, None, None, state, '_', 0)
1482
 
        self.assertEntryEqual(None, None, None, state, '_\xc3\xa5', 0)
1483
 
        self.assertEntryEqual(None, None, None, state, 'a/b', 0)
1484
 
        self.assertEntryEqual(None, None, None, state, 'c/d', 0)
 
1618
        self.assertEntryEqual(None, None, None, state, b'_', 0)
 
1619
        self.assertEntryEqual(None, None, None, state, b'_\xc3\xa5', 0)
 
1620
        self.assertEntryEqual(None, None, None, state, b'a/b', 0)
 
1621
        self.assertEntryEqual(None, None, None, state, b'c/d', 0)
1485
1622
 
1486
1623
    def test_get_entry_uninitialized(self):
1487
1624
        """Calling get_entry will load data if it needs to"""
1498
1635
                             state._header_state)
1499
1636
            self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
1500
1637
                             state._dirblock_state)
1501
 
            self.assertEntryEqual('', '', 'a-root-value', state, '', 0)
 
1638
            self.assertEntryEqual(b'', b'', b'a-root-value', state, b'', 0)
1502
1639
        finally:
1503
1640
            state.unlock()
1504
1641
 
1528
1665
 
1529
1666
        :return: The dirstate, still write-locked.
1530
1667
        """
1531
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1532
 
        null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
 
1668
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1669
        null_sha = b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
1533
1670
        NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1534
 
        root_entry = ('', '', 'a-root-value'), [
1535
 
            ('d', '', 0, False, packed_stat),
1536
 
            ('d', '', 0, False, 'parent-revid'),
1537
 
            ]
1538
 
        a_entry = ('', 'a', 'a-dir'), [
1539
 
            ('d', '', 0, False, packed_stat),
1540
 
            ('d', '', 0, False, 'parent-revid'),
1541
 
            ]
1542
 
        b_entry = ('', 'b', 'b-dir'), [
1543
 
            ('d', '', 0, False, packed_stat),
1544
 
            ('d', '', 0, False, 'parent-revid'),
1545
 
            ]
1546
 
        c_entry = ('', 'c', 'c-file'), [
1547
 
            ('f', null_sha, 10, False, packed_stat),
1548
 
            ('r', 'b/j', 0, False, ''),
1549
 
            ]
1550
 
        d_entry = ('', 'd', 'd-file'), [
1551
 
            ('f', null_sha, 20, False, packed_stat),
1552
 
            ('f', 'd', 20, False, 'parent-revid'),
1553
 
            ]
1554
 
        e_entry = ('a', 'e', 'e-dir'), [
1555
 
            ('d', '', 0, False, packed_stat),
1556
 
            ('d', '', 0, False, 'parent-revid'),
1557
 
            ]
1558
 
        f_entry = ('a', 'f', 'f-file'), [
1559
 
            ('f', null_sha, 30, False, packed_stat),
1560
 
            ('f', 'f', 20, False, 'parent-revid'),
1561
 
            ]
1562
 
        g_entry = ('b', 'g', 'g-file'), [
1563
 
            ('f', null_sha, 30, False, packed_stat),
1564
 
            NULL_PARENT_DETAILS,
1565
 
            ]
1566
 
        h_entry1 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file1'), [
1567
 
            ('f', null_sha, 40, False, packed_stat),
1568
 
            NULL_PARENT_DETAILS,
1569
 
            ]
1570
 
        h_entry2 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file2'), [
1571
 
            NULL_PARENT_DETAILS,
1572
 
            ('f', 'h', 20, False, 'parent-revid'),
1573
 
            ]
1574
 
        i_entry = ('b', 'i', 'i-file'), [
1575
 
            NULL_PARENT_DETAILS,
1576
 
            ('f', 'h', 20, False, 'parent-revid'),
1577
 
            ]
1578
 
        j_entry = ('b', 'j', 'c-file'), [
1579
 
            ('r', 'c', 0, False, ''),
1580
 
            ('f', 'j', 20, False, 'parent-revid'),
 
1671
        root_entry = (b'', b'', b'a-root-value'), [
 
1672
            (b'd', b'', 0, False, packed_stat),
 
1673
            (b'd', b'', 0, False, b'parent-revid'),
 
1674
            ]
 
1675
        a_entry = (b'', b'a', b'a-dir'), [
 
1676
            (b'd', b'', 0, False, packed_stat),
 
1677
            (b'd', b'', 0, False, b'parent-revid'),
 
1678
            ]
 
1679
        b_entry = (b'', b'b', b'b-dir'), [
 
1680
            (b'd', b'', 0, False, packed_stat),
 
1681
            (b'd', b'', 0, False, b'parent-revid'),
 
1682
            ]
 
1683
        c_entry = (b'', b'c', b'c-file'), [
 
1684
            (b'f', null_sha, 10, False, packed_stat),
 
1685
            (b'r', b'b/j', 0, False, b''),
 
1686
            ]
 
1687
        d_entry = (b'', b'd', b'd-file'), [
 
1688
            (b'f', null_sha, 20, False, packed_stat),
 
1689
            (b'f', b'd', 20, False, b'parent-revid'),
 
1690
            ]
 
1691
        e_entry = (b'a', b'e', b'e-dir'), [
 
1692
            (b'd', b'', 0, False, packed_stat),
 
1693
            (b'd', b'', 0, False, b'parent-revid'),
 
1694
            ]
 
1695
        f_entry = (b'a', b'f', b'f-file'), [
 
1696
            (b'f', null_sha, 30, False, packed_stat),
 
1697
            (b'f', b'f', 20, False, b'parent-revid'),
 
1698
            ]
 
1699
        g_entry = (b'b', b'g', b'g-file'), [
 
1700
            (b'f', null_sha, 30, False, packed_stat),
 
1701
            NULL_PARENT_DETAILS,
 
1702
            ]
 
1703
        h_entry1 = (b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file1'), [
 
1704
            (b'f', null_sha, 40, False, packed_stat),
 
1705
            NULL_PARENT_DETAILS,
 
1706
            ]
 
1707
        h_entry2 = (b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file2'), [
 
1708
            NULL_PARENT_DETAILS,
 
1709
            (b'f', b'h', 20, False, b'parent-revid'),
 
1710
            ]
 
1711
        i_entry = (b'b', b'i', b'i-file'), [
 
1712
            NULL_PARENT_DETAILS,
 
1713
            (b'f', b'h', 20, False, b'parent-revid'),
 
1714
            ]
 
1715
        j_entry = (b'b', b'j', b'c-file'), [
 
1716
            (b'r', b'c', 0, False, b''),
 
1717
            (b'f', b'j', 20, False, b'parent-revid'),
1581
1718
            ]
1582
1719
        dirblocks = []
1583
 
        dirblocks.append(('', [root_entry]))
1584
 
        dirblocks.append(('', [a_entry, b_entry, c_entry, d_entry]))
1585
 
        dirblocks.append(('a', [e_entry, f_entry]))
1586
 
        dirblocks.append(('b', [g_entry, h_entry1, h_entry2, i_entry, j_entry]))
 
1720
        dirblocks.append((b'', [root_entry]))
 
1721
        dirblocks.append((b'', [a_entry, b_entry, c_entry, d_entry]))
 
1722
        dirblocks.append((b'a', [e_entry, f_entry]))
 
1723
        dirblocks.append(
 
1724
            (b'b', [g_entry, h_entry1, h_entry2, i_entry, j_entry]))
1587
1725
        state = dirstate.DirState.initialize('dirstate')
1588
1726
        state._validate()
1589
1727
        try:
1590
 
            state._set_data(['parent'], dirblocks)
 
1728
            state._set_data([b'parent'], dirblocks)
1591
1729
        except:
1592
1730
            state.unlock()
1593
1731
            raise
1597
1735
        state, dirblocks = self.create_dirstate_with_two_trees()
1598
1736
        self.addCleanup(state.unlock)
1599
1737
        expected_result = []
1600
 
        expected_result.append(dirblocks[3][1][2]) # h2
1601
 
        expected_result.append(dirblocks[3][1][3]) # i
1602
 
        expected_result.append(dirblocks[3][1][4]) # j
 
1738
        expected_result.append(dirblocks[3][1][2])  # h2
 
1739
        expected_result.append(dirblocks[3][1][3])  # i
 
1740
        expected_result.append(dirblocks[3][1][4])  # j
1603
1741
        self.assertEqual(expected_result,
1604
 
            list(state._iter_child_entries(1, 'b')))
 
1742
                         list(state._iter_child_entries(1, b'b')))
1605
1743
 
1606
1744
    def test_iter_child_root(self):
1607
1745
        state, dirblocks = self.create_dirstate_with_two_trees()
1608
1746
        self.addCleanup(state.unlock)
1609
1747
        expected_result = []
1610
 
        expected_result.append(dirblocks[1][1][0]) # a
1611
 
        expected_result.append(dirblocks[1][1][1]) # b
1612
 
        expected_result.append(dirblocks[1][1][3]) # d
1613
 
        expected_result.append(dirblocks[2][1][0]) # e
1614
 
        expected_result.append(dirblocks[2][1][1]) # f
1615
 
        expected_result.append(dirblocks[3][1][2]) # h2
1616
 
        expected_result.append(dirblocks[3][1][3]) # i
1617
 
        expected_result.append(dirblocks[3][1][4]) # j
 
1748
        expected_result.append(dirblocks[1][1][0])  # a
 
1749
        expected_result.append(dirblocks[1][1][1])  # b
 
1750
        expected_result.append(dirblocks[1][1][3])  # d
 
1751
        expected_result.append(dirblocks[2][1][0])  # e
 
1752
        expected_result.append(dirblocks[2][1][1])  # f
 
1753
        expected_result.append(dirblocks[3][1][2])  # h2
 
1754
        expected_result.append(dirblocks[3][1][3])  # i
 
1755
        expected_result.append(dirblocks[3][1][4])  # j
1618
1756
        self.assertEqual(expected_result,
1619
 
            list(state._iter_child_entries(1, '')))
 
1757
                         list(state._iter_child_entries(1, b'')))
1620
1758
 
1621
1759
 
1622
1760
class TestDirstateSortOrder(tests.TestCaseWithTransport):
1631
1769
        """
1632
1770
        dirs = ['a', 'a/a', 'a/a/a', 'a/a/a/a',
1633
1771
                'a-a', 'a/a-a', 'a/a/a-a', 'a/a/a/a-a',
1634
 
               ]
1635
 
        null_sha = ''
 
1772
                ]
 
1773
        null_sha = b''
1636
1774
        state = dirstate.DirState.initialize('dirstate')
1637
1775
        self.addCleanup(state.unlock)
1638
1776
 
1639
1777
        fake_stat = os.stat('dirstate')
1640
1778
        for d in dirs:
1641
 
            d_id = d.replace('/', '_')+'-id'
 
1779
            d_id = d.encode('utf-8').replace(b'/', b'_') + b'-id'
1642
1780
            file_path = d + '/f'
1643
 
            file_id = file_path.replace('/', '_')+'-id'
 
1781
            file_id = file_path.encode('utf-8').replace(b'/', b'_') + b'-id'
1644
1782
            state.add(d, d_id, 'directory', fake_stat, null_sha)
1645
1783
            state.add(file_path, file_id, 'file', fake_stat, null_sha)
1646
1784
 
1647
 
        expected = ['', '', 'a',
1648
 
                'a/a', 'a/a/a', 'a/a/a/a',
1649
 
                'a/a/a/a-a', 'a/a/a-a', 'a/a-a', 'a-a',
1650
 
               ]
1651
 
        split = lambda p:p.split('/')
 
1785
        expected = [b'', b'', b'a',
 
1786
                    b'a/a', b'a/a/a', b'a/a/a/a',
 
1787
                    b'a/a/a/a-a', b'a/a/a-a', b'a/a-a', b'a-a',
 
1788
                    ]
 
1789
 
 
1790
        def split(p): return p.split(b'/')
1652
1791
        self.assertEqual(sorted(expected, key=split), expected)
1653
1792
        dirblock_names = [d[0] for d in state._dirblocks]
1654
1793
        self.assertEqual(expected, dirblock_names)
1656
1795
    def test_set_parent_trees_correct_order(self):
1657
1796
        """After calling set_parent_trees() we should maintain the order."""
1658
1797
        dirs = ['a', 'a-a', 'a/a']
1659
 
        null_sha = ''
 
1798
        null_sha = b''
1660
1799
        state = dirstate.DirState.initialize('dirstate')
1661
1800
        self.addCleanup(state.unlock)
1662
1801
 
1663
1802
        fake_stat = os.stat('dirstate')
1664
1803
        for d in dirs:
1665
 
            d_id = d.replace('/', '_')+'-id'
 
1804
            d_id = d.encode('utf-8').replace(b'/', b'_') + b'-id'
1666
1805
            file_path = d + '/f'
1667
 
            file_id = file_path.replace('/', '_')+'-id'
 
1806
            file_id = file_path.encode('utf-8').replace(b'/', b'_') + b'-id'
1668
1807
            state.add(d, d_id, 'directory', fake_stat, null_sha)
1669
1808
            state.add(file_path, file_id, 'file', fake_stat, null_sha)
1670
1809
 
1671
 
        expected = ['', '', 'a', 'a/a', 'a-a']
 
1810
        expected = [b'', b'', b'a', b'a/a', b'a-a']
1672
1811
        dirblock_names = [d[0] for d in state._dirblocks]
1673
1812
        self.assertEqual(expected, dirblock_names)
1674
1813
 
1684
1823
class InstrumentedDirState(dirstate.DirState):
1685
1824
    """An DirState with instrumented sha1 functionality."""
1686
1825
 
1687
 
    def __init__(self, path, sha1_provider):
1688
 
        super(InstrumentedDirState, self).__init__(path, sha1_provider)
 
1826
    def __init__(self, path, sha1_provider, worth_saving_limit=0,
 
1827
                 use_filesystem_for_exec=True):
 
1828
        super(InstrumentedDirState, self).__init__(
 
1829
            path, sha1_provider, worth_saving_limit=worth_saving_limit,
 
1830
            use_filesystem_for_exec=use_filesystem_for_exec)
1689
1831
        self._time_offset = 0
1690
1832
        self._log = []
1691
1833
        # member is dynamically set in DirState.__init__ to turn on trace
1738
1880
    @staticmethod
1739
1881
    def from_stat(st):
1740
1882
        return _FakeStat(st.st_size, st.st_mtime, st.st_ctime, st.st_dev,
1741
 
            st.st_ino, st.st_mode)
 
1883
                         st.st_ino, st.st_mode)
1742
1884
 
1743
1885
 
1744
1886
class TestPackStat(tests.TestCaseWithTransport):
1748
1890
        self.assertEqual(expected, dirstate.pack_stat(stat_value))
1749
1891
 
1750
1892
    def test_pack_stat_int(self):
1751
 
        st = _FakeStat(6859L, 1172758614, 1172758617, 777L, 6499538L, 0100644)
 
1893
        st = _FakeStat(6859, 1172758614, 1172758617, 777, 6499538, 0o100644)
1752
1894
        # Make sure that all parameters have an impact on the packed stat.
1753
 
        self.assertPackStat('AAAay0Xm4FZF5uBZAAADCQBjLNIAAIGk', st)
1754
 
        st.st_size = 7000L
 
1895
        self.assertPackStat(b'AAAay0Xm4FZF5uBZAAADCQBjLNIAAIGk', st)
 
1896
        st.st_size = 7000
1755
1897
        #                ay0 => bWE
1756
 
        self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
 
1898
        self.assertPackStat(b'AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1757
1899
        st.st_mtime = 1172758620
1758
1900
        #                     4FZ => 4Fx
1759
 
        self.assertPackStat('AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
 
1901
        self.assertPackStat(b'AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
1760
1902
        st.st_ctime = 1172758630
1761
1903
        #                          uBZ => uBm
1762
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1763
 
        st.st_dev = 888L
 
1904
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1905
        st.st_dev = 888
1764
1906
        #                                DCQ => DeA
1765
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNIAAIGk', st)
1766
 
        st.st_ino = 6499540L
 
1907
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADeABjLNIAAIGk', st)
 
1908
        st.st_ino = 6499540
1767
1909
        #                                     LNI => LNQ
1768
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIGk', st)
1769
 
        st.st_mode = 0100744
 
1910
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADeABjLNQAAIGk', st)
 
1911
        st.st_mode = 0o100744
1770
1912
        #                                          IGk => IHk
1771
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIHk', st)
 
1913
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADeABjLNQAAIHk', st)
1772
1914
 
1773
1915
    def test_pack_stat_float(self):
1774
1916
        """On some platforms mtime and ctime are floats.
1776
1918
        Make sure we don't get warnings or errors, and that we ignore changes <
1777
1919
        1s
1778
1920
        """
1779
 
        st = _FakeStat(7000L, 1172758614.0, 1172758617.0,
1780
 
                       777L, 6499538L, 0100644)
 
1921
        st = _FakeStat(7000, 1172758614.0, 1172758617.0,
 
1922
                       777, 6499538, 0o100644)
1781
1923
        # These should all be the same as the integer counterparts
1782
 
        self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
 
1924
        self.assertPackStat(b'AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1783
1925
        st.st_mtime = 1172758620.0
1784
1926
        #                     FZF5 => FxF5
1785
 
        self.assertPackStat('AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
 
1927
        self.assertPackStat(b'AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
1786
1928
        st.st_ctime = 1172758630.0
1787
1929
        #                          uBZ => uBm
1788
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1930
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1789
1931
        # fractional seconds are discarded, so no change from above
1790
1932
        st.st_mtime = 1172758620.453
1791
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1933
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1792
1934
        st.st_ctime = 1172758630.228
1793
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1935
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1794
1936
 
1795
1937
 
1796
1938
class TestBisect(TestCaseWithDirState):
1874
2016
        tree, state, expected = self.create_basic_dirstate()
1875
2017
 
1876
2018
        # Bisect should return the rows for the specified files.
1877
 
        self.assertBisect(expected, [['']], state, [''])
1878
 
        self.assertBisect(expected, [['a']], state, ['a'])
1879
 
        self.assertBisect(expected, [['b']], state, ['b'])
1880
 
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
1881
 
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1882
 
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1883
 
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
1884
 
        self.assertBisect(expected, [['f']], state, ['f'])
 
2019
        self.assertBisect(expected, [[b'']], state, [b''])
 
2020
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2021
        self.assertBisect(expected, [[b'b']], state, [b'b'])
 
2022
        self.assertBisect(expected, [[b'b/c']], state, [b'b/c'])
 
2023
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2024
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2025
        self.assertBisect(expected, [[b'b-c']], state, [b'b-c'])
 
2026
        self.assertBisect(expected, [[b'f']], state, [b'f'])
1885
2027
 
1886
2028
    def test_bisect_multi(self):
1887
2029
        """Bisect can be used to find multiple records at the same time."""
1888
2030
        tree, state, expected = self.create_basic_dirstate()
1889
2031
        # Bisect should be capable of finding multiple entries at the same time
1890
 
        self.assertBisect(expected, [['a'], ['b'], ['f']],
1891
 
                          state, ['a', 'b', 'f'])
1892
 
        self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1893
 
                          state, ['f', 'b/d', 'b/d/e'])
1894
 
        self.assertBisect(expected, [['b'], ['b-c'], ['b/c']],
1895
 
                          state, ['b', 'b-c', 'b/c'])
 
2032
        self.assertBisect(expected, [[b'a'], [b'b'], [b'f']],
 
2033
                          state, [b'a', b'b', b'f'])
 
2034
        self.assertBisect(expected, [[b'f'], [b'b/d'], [b'b/d/e']],
 
2035
                          state, [b'f', b'b/d', b'b/d/e'])
 
2036
        self.assertBisect(expected, [[b'b'], [b'b-c'], [b'b/c']],
 
2037
                          state, [b'b', b'b-c', b'b/c'])
1896
2038
 
1897
2039
    def test_bisect_one_page(self):
1898
2040
        """Test bisect when there is only 1 page to read"""
1899
2041
        tree, state, expected = self.create_basic_dirstate()
1900
2042
        state._bisect_page_size = 5000
1901
 
        self.assertBisect(expected,[['']], state, [''])
1902
 
        self.assertBisect(expected,[['a']], state, ['a'])
1903
 
        self.assertBisect(expected,[['b']], state, ['b'])
1904
 
        self.assertBisect(expected,[['b/c']], state, ['b/c'])
1905
 
        self.assertBisect(expected,[['b/d']], state, ['b/d'])
1906
 
        self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
1907
 
        self.assertBisect(expected,[['b-c']], state, ['b-c'])
1908
 
        self.assertBisect(expected,[['f']], state, ['f'])
1909
 
        self.assertBisect(expected,[['a'], ['b'], ['f']],
1910
 
                          state, ['a', 'b', 'f'])
1911
 
        self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
1912
 
                          state, ['b/d', 'b/d/e', 'f'])
1913
 
        self.assertBisect(expected, [['b'], ['b/c'], ['b-c']],
1914
 
                          state, ['b', 'b/c', 'b-c'])
 
2043
        self.assertBisect(expected, [[b'']], state, [b''])
 
2044
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2045
        self.assertBisect(expected, [[b'b']], state, [b'b'])
 
2046
        self.assertBisect(expected, [[b'b/c']], state, [b'b/c'])
 
2047
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2048
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2049
        self.assertBisect(expected, [[b'b-c']], state, [b'b-c'])
 
2050
        self.assertBisect(expected, [[b'f']], state, [b'f'])
 
2051
        self.assertBisect(expected, [[b'a'], [b'b'], [b'f']],
 
2052
                          state, [b'a', b'b', b'f'])
 
2053
        self.assertBisect(expected, [[b'b/d'], [b'b/d/e'], [b'f']],
 
2054
                          state, [b'b/d', b'b/d/e', b'f'])
 
2055
        self.assertBisect(expected, [[b'b'], [b'b/c'], [b'b-c']],
 
2056
                          state, [b'b', b'b/c', b'b-c'])
1915
2057
 
1916
2058
    def test_bisect_duplicate_paths(self):
1917
2059
        """When bisecting for a path, handle multiple entries."""
1918
2060
        tree, state, expected = self.create_duplicated_dirstate()
1919
2061
 
1920
2062
        # Now make sure that both records are properly returned.
1921
 
        self.assertBisect(expected, [['']], state, [''])
1922
 
        self.assertBisect(expected, [['a', 'a2']], state, ['a'])
1923
 
        self.assertBisect(expected, [['b', 'b2']], state, ['b'])
1924
 
        self.assertBisect(expected, [['b/c', 'b/c2']], state, ['b/c'])
1925
 
        self.assertBisect(expected, [['b/d', 'b/d2']], state, ['b/d'])
1926
 
        self.assertBisect(expected, [['b/d/e', 'b/d/e2']],
1927
 
                          state, ['b/d/e'])
1928
 
        self.assertBisect(expected, [['b-c', 'b-c2']], state, ['b-c'])
1929
 
        self.assertBisect(expected, [['f', 'f2']], state, ['f'])
 
2063
        self.assertBisect(expected, [[b'']], state, [b''])
 
2064
        self.assertBisect(expected, [[b'a', b'a2']], state, [b'a'])
 
2065
        self.assertBisect(expected, [[b'b', b'b2']], state, [b'b'])
 
2066
        self.assertBisect(expected, [[b'b/c', b'b/c2']], state, [b'b/c'])
 
2067
        self.assertBisect(expected, [[b'b/d', b'b/d2']], state, [b'b/d'])
 
2068
        self.assertBisect(expected, [[b'b/d/e', b'b/d/e2']],
 
2069
                          state, [b'b/d/e'])
 
2070
        self.assertBisect(expected, [[b'b-c', b'b-c2']], state, [b'b-c'])
 
2071
        self.assertBisect(expected, [[b'f', b'f2']], state, [b'f'])
1930
2072
 
1931
2073
    def test_bisect_page_size_too_small(self):
1932
2074
        """If the page size is too small, we will auto increase it."""
1933
2075
        tree, state, expected = self.create_basic_dirstate()
1934
2076
        state._bisect_page_size = 50
1935
 
        self.assertBisect(expected, [None], state, ['b/e'])
1936
 
        self.assertBisect(expected, [['a']], state, ['a'])
1937
 
        self.assertBisect(expected, [['b']], state, ['b'])
1938
 
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
1939
 
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1940
 
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1941
 
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
1942
 
        self.assertBisect(expected, [['f']], state, ['f'])
 
2077
        self.assertBisect(expected, [None], state, [b'b/e'])
 
2078
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2079
        self.assertBisect(expected, [[b'b']], state, [b'b'])
 
2080
        self.assertBisect(expected, [[b'b/c']], state, [b'b/c'])
 
2081
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2082
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2083
        self.assertBisect(expected, [[b'b-c']], state, [b'b-c'])
 
2084
        self.assertBisect(expected, [[b'f']], state, [b'f'])
1943
2085
 
1944
2086
    def test_bisect_missing(self):
1945
2087
        """Test that bisect return None if it cannot find a path."""
1946
2088
        tree, state, expected = self.create_basic_dirstate()
1947
 
        self.assertBisect(expected, [None], state, ['foo'])
1948
 
        self.assertBisect(expected, [None], state, ['b/foo'])
1949
 
        self.assertBisect(expected, [None], state, ['bar/foo'])
1950
 
        self.assertBisect(expected, [None], state, ['b-c/foo'])
 
2089
        self.assertBisect(expected, [None], state, [b'foo'])
 
2090
        self.assertBisect(expected, [None], state, [b'b/foo'])
 
2091
        self.assertBisect(expected, [None], state, [b'bar/foo'])
 
2092
        self.assertBisect(expected, [None], state, [b'b-c/foo'])
1951
2093
 
1952
 
        self.assertBisect(expected, [['a'], None, ['b/d']],
1953
 
                          state, ['a', 'foo', 'b/d'])
 
2094
        self.assertBisect(expected, [[b'a'], None, [b'b/d']],
 
2095
                          state, [b'a', b'foo', b'b/d'])
1954
2096
 
1955
2097
    def test_bisect_rename(self):
1956
2098
        """Check that we find a renamed row."""
1957
2099
        tree, state, expected = self.create_renamed_dirstate()
1958
2100
 
1959
2101
        # Search for the pre and post renamed entries
1960
 
        self.assertBisect(expected, [['a']], state, ['a'])
1961
 
        self.assertBisect(expected, [['b/g']], state, ['b/g'])
1962
 
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1963
 
        self.assertBisect(expected, [['h']], state, ['h'])
 
2102
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2103
        self.assertBisect(expected, [[b'b/g']], state, [b'b/g'])
 
2104
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2105
        self.assertBisect(expected, [[b'h']], state, [b'h'])
1964
2106
 
1965
2107
        # What about b/d/e? shouldn't that also get 2 directory entries?
1966
 
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1967
 
        self.assertBisect(expected, [['h/e']], state, ['h/e'])
 
2108
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2109
        self.assertBisect(expected, [[b'h/e']], state, [b'h/e'])
1968
2110
 
1969
2111
    def test_bisect_dirblocks(self):
1970
2112
        tree, state, expected = self.create_duplicated_dirstate()
1971
2113
        self.assertBisectDirBlocks(expected,
1972
 
            [['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2']],
1973
 
            state, [''])
1974
 
        self.assertBisectDirBlocks(expected,
1975
 
            [['b/c', 'b/c2', 'b/d', 'b/d2']], state, ['b'])
1976
 
        self.assertBisectDirBlocks(expected,
1977
 
            [['b/d/e', 'b/d/e2']], state, ['b/d'])
1978
 
        self.assertBisectDirBlocks(expected,
1979
 
            [['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2'],
1980
 
             ['b/c', 'b/c2', 'b/d', 'b/d2'],
1981
 
             ['b/d/e', 'b/d/e2'],
1982
 
            ], state, ['', 'b', 'b/d'])
 
2114
                                   [[b'', b'a', b'a2', b'b', b'b2',
 
2115
                                       b'b-c', b'b-c2', b'f', b'f2']],
 
2116
                                   state, [b''])
 
2117
        self.assertBisectDirBlocks(expected,
 
2118
                                   [[b'b/c', b'b/c2', b'b/d', b'b/d2']], state, [b'b'])
 
2119
        self.assertBisectDirBlocks(expected,
 
2120
                                   [[b'b/d/e', b'b/d/e2']], state, [b'b/d'])
 
2121
        self.assertBisectDirBlocks(expected,
 
2122
                                   [[b'', b'a', b'a2', b'b', b'b2', b'b-c', b'b-c2', b'f', b'f2'],
 
2123
                                    [b'b/c', b'b/c2', b'b/d', b'b/d2'],
 
2124
                                       [b'b/d/e', b'b/d/e2'],
 
2125
                                    ], state, [b'', b'b', b'b/d'])
1983
2126
 
1984
2127
    def test_bisect_dirblocks_missing(self):
1985
2128
        tree, state, expected = self.create_basic_dirstate()
1986
 
        self.assertBisectDirBlocks(expected, [['b/d/e'], None],
1987
 
            state, ['b/d', 'b/e'])
 
2129
        self.assertBisectDirBlocks(expected, [[b'b/d/e'], None],
 
2130
                                   state, [b'b/d', b'b/e'])
1988
2131
        # Files don't show up in this search
1989
 
        self.assertBisectDirBlocks(expected, [None], state, ['a'])
1990
 
        self.assertBisectDirBlocks(expected, [None], state, ['b/c'])
1991
 
        self.assertBisectDirBlocks(expected, [None], state, ['c'])
1992
 
        self.assertBisectDirBlocks(expected, [None], state, ['b/d/e'])
1993
 
        self.assertBisectDirBlocks(expected, [None], state, ['f'])
 
2132
        self.assertBisectDirBlocks(expected, [None], state, [b'a'])
 
2133
        self.assertBisectDirBlocks(expected, [None], state, [b'b/c'])
 
2134
        self.assertBisectDirBlocks(expected, [None], state, [b'c'])
 
2135
        self.assertBisectDirBlocks(expected, [None], state, [b'b/d/e'])
 
2136
        self.assertBisectDirBlocks(expected, [None], state, [b'f'])
1994
2137
 
1995
2138
    def test_bisect_recursive_each(self):
1996
2139
        tree, state, expected = self.create_basic_dirstate()
1997
 
        self.assertBisectRecursive(expected, ['a'], state, ['a'])
1998
 
        self.assertBisectRecursive(expected, ['b/c'], state, ['b/c'])
1999
 
        self.assertBisectRecursive(expected, ['b/d/e'], state, ['b/d/e'])
2000
 
        self.assertBisectRecursive(expected, ['b-c'], state, ['b-c'])
2001
 
        self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
2002
 
                                   state, ['b/d'])
2003
 
        self.assertBisectRecursive(expected, ['b', 'b/c', 'b/d', 'b/d/e'],
2004
 
                                   state, ['b'])
2005
 
        self.assertBisectRecursive(expected, ['', 'a', 'b', 'b-c', 'f', 'b/c',
2006
 
                                              'b/d', 'b/d/e'],
2007
 
                                   state, [''])
 
2140
        self.assertBisectRecursive(expected, [b'a'], state, [b'a'])
 
2141
        self.assertBisectRecursive(expected, [b'b/c'], state, [b'b/c'])
 
2142
        self.assertBisectRecursive(expected, [b'b/d/e'], state, [b'b/d/e'])
 
2143
        self.assertBisectRecursive(expected, [b'b-c'], state, [b'b-c'])
 
2144
        self.assertBisectRecursive(expected, [b'b/d', b'b/d/e'],
 
2145
                                   state, [b'b/d'])
 
2146
        self.assertBisectRecursive(expected, [b'b', b'b/c', b'b/d', b'b/d/e'],
 
2147
                                   state, [b'b'])
 
2148
        self.assertBisectRecursive(expected, [b'', b'a', b'b', b'b-c', b'f', b'b/c',
 
2149
                                              b'b/d', b'b/d/e'],
 
2150
                                   state, [b''])
2008
2151
 
2009
2152
    def test_bisect_recursive_multiple(self):
2010
2153
        tree, state, expected = self.create_basic_dirstate()
2011
 
        self.assertBisectRecursive(expected, ['a', 'b/c'], state, ['a', 'b/c'])
2012
 
        self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
2013
 
                                   state, ['b/d', 'b/d/e'])
 
2154
        self.assertBisectRecursive(
 
2155
            expected, [b'a', b'b/c'], state, [b'a', b'b/c'])
 
2156
        self.assertBisectRecursive(expected, [b'b/d', b'b/d/e'],
 
2157
                                   state, [b'b/d', b'b/d/e'])
2014
2158
 
2015
2159
    def test_bisect_recursive_missing(self):
2016
2160
        tree, state, expected = self.create_basic_dirstate()
2017
 
        self.assertBisectRecursive(expected, [], state, ['d'])
2018
 
        self.assertBisectRecursive(expected, [], state, ['b/e'])
2019
 
        self.assertBisectRecursive(expected, [], state, ['g'])
2020
 
        self.assertBisectRecursive(expected, ['a'], state, ['a', 'g'])
 
2161
        self.assertBisectRecursive(expected, [], state, [b'd'])
 
2162
        self.assertBisectRecursive(expected, [], state, [b'b/e'])
 
2163
        self.assertBisectRecursive(expected, [], state, [b'g'])
 
2164
        self.assertBisectRecursive(expected, [b'a'], state, [b'a', b'g'])
2021
2165
 
2022
2166
    def test_bisect_recursive_renamed(self):
2023
2167
        tree, state, expected = self.create_renamed_dirstate()
2024
2168
 
2025
2169
        # Looking for either renamed item should find the other
2026
 
        self.assertBisectRecursive(expected, ['a', 'b/g'], state, ['a'])
2027
 
        self.assertBisectRecursive(expected, ['a', 'b/g'], state, ['b/g'])
 
2170
        self.assertBisectRecursive(expected, [b'a', b'b/g'], state, [b'a'])
 
2171
        self.assertBisectRecursive(expected, [b'a', b'b/g'], state, [b'b/g'])
2028
2172
        # Looking in the containing directory should find the rename target,
2029
2173
        # and anything in a subdir of the renamed target.
2030
 
        self.assertBisectRecursive(expected, ['a', 'b', 'b/c', 'b/d',
2031
 
                                              'b/d/e', 'b/g', 'h', 'h/e'],
2032
 
                                   state, ['b'])
 
2174
        self.assertBisectRecursive(expected, [b'a', b'b', b'b/c', b'b/d',
 
2175
                                              b'b/d/e', b'b/g', b'h', b'h/e'],
 
2176
                                   state, [b'b'])
2033
2177
 
2034
2178
 
2035
2179
class TestDirstateValidation(TestCaseWithDirState):
2052
2196
        # we're appending to the dirblock, but this name comes before some of
2053
2197
        # the existing names; that's wrong
2054
2198
        last_dirblock[1].append(
2055
 
            (('h', 'aaaa', 'a-id'),
2056
 
             [('a', '', 0, False, ''),
2057
 
              ('a', '', 0, False, '')]))
 
2199
            ((b'h', b'aaaa', b'a-id'),
 
2200
             [(b'a', b'', 0, False, b''),
 
2201
              (b'a', b'', 0, False, b'')]))
2058
2202
        e = self.assertRaises(AssertionError,
2059
 
            state._validate)
 
2203
                              state._validate)
2060
2204
        self.assertContainsRe(str(e), 'not sorted')
2061
2205
 
2062
2206
    def test_dirblock_name_mismatch(self):
2065
2209
        last_dirblock = state._dirblocks[-1]
2066
2210
        # add an entry with the wrong directory name
2067
2211
        last_dirblock[1].append(
2068
 
            (('', 'z', 'a-id'),
2069
 
             [('a', '', 0, False, ''),
2070
 
              ('a', '', 0, False, '')]))
 
2212
            ((b'', b'z', b'a-id'),
 
2213
             [(b'a', b'', 0, False, b''),
 
2214
              (b'a', b'', 0, False, b'')]))
2071
2215
        e = self.assertRaises(AssertionError,
2072
 
            state._validate)
 
2216
                              state._validate)
2073
2217
        self.assertContainsRe(str(e),
2074
 
            "doesn't match directory name")
 
2218
                              "doesn't match directory name")
2075
2219
 
2076
2220
    def test_dirblock_missing_rename(self):
2077
2221
        tree, state, expected = self.create_renamed_dirstate()
2080
2224
        # make another entry for a-id, without a correct 'r' pointer to
2081
2225
        # the real occurrence in the working tree
2082
2226
        last_dirblock[1].append(
2083
 
            (('h', 'z', 'a-id'),
2084
 
             [('a', '', 0, False, ''),
2085
 
              ('a', '', 0, False, '')]))
 
2227
            ((b'h', b'z', b'a-id'),
 
2228
             [(b'a', b'', 0, False, b''),
 
2229
              (b'a', b'', 0, False, b'')]))
2086
2230
        e = self.assertRaises(AssertionError,
2087
 
            state._validate)
 
2231
                              state._validate)
2088
2232
        self.assertContainsRe(str(e),
2089
 
            'file a-id is absent in row')
 
2233
                              'file a-id is absent in row')
2090
2234
 
2091
2235
 
2092
2236
class TestDirstateTreeReference(TestCaseWithDirState):
2093
2237
 
2094
2238
    def test_reference_revision_is_none(self):
2095
 
        tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
 
2239
        tree = self.make_branch_and_tree('tree', format='development-subtree')
2096
2240
        subtree = self.make_branch_and_tree('tree/subtree',
2097
 
                            format='dirstate-with-subtree')
2098
 
        subtree.set_root_id('subtree')
 
2241
                                            format='development-subtree')
 
2242
        subtree.set_root_id(b'subtree')
2099
2243
        tree.add_reference(subtree)
2100
2244
        tree.add('subtree')
2101
2245
        state = dirstate.DirState.from_tree(tree, 'dirstate')
2102
 
        key = ('', 'subtree', 'subtree')
2103
 
        expected = ('', [(key,
2104
 
            [('t', '', 0, False, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])])
 
2246
        key = (b'', b'subtree', b'subtree')
 
2247
        expected = (b'', [(key,
 
2248
                           [(b't', b'', 0, False, b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])])
2105
2249
 
2106
2250
        try:
2107
2251
            self.assertEqual(expected, state._find_block(key))
2120
2264
 
2121
2265
    def test_discard_one_parent(self):
2122
2266
        # No-op
2123
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2124
 
        root_entry_direntry = ('', '', 'a-root-value'), [
2125
 
            ('d', '', 0, False, packed_stat),
2126
 
            ('d', '', 0, False, packed_stat),
 
2267
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
2268
        root_entry_direntry = (b'', b'', b'a-root-value'), [
 
2269
            (b'd', b'', 0, False, packed_stat),
 
2270
            (b'd', b'', 0, False, packed_stat),
2127
2271
            ]
2128
2272
        dirblocks = []
2129
 
        dirblocks.append(('', [root_entry_direntry]))
2130
 
        dirblocks.append(('', []))
 
2273
        dirblocks.append((b'', [root_entry_direntry]))
 
2274
        dirblocks.append((b'', []))
2131
2275
 
2132
2276
        state = self.create_empty_dirstate()
2133
2277
        self.addCleanup(state.unlock)
2134
 
        state._set_data(['parent-id'], dirblocks[:])
 
2278
        state._set_data([b'parent-id'], dirblocks[:])
2135
2279
        state._validate()
2136
2280
 
2137
2281
        state._discard_merge_parents()
2140
2284
 
2141
2285
    def test_discard_simple(self):
2142
2286
        # No-op
2143
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2144
 
        root_entry_direntry = ('', '', 'a-root-value'), [
2145
 
            ('d', '', 0, False, packed_stat),
2146
 
            ('d', '', 0, False, packed_stat),
2147
 
            ('d', '', 0, False, packed_stat),
 
2287
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
2288
        root_entry_direntry = (b'', b'', b'a-root-value'), [
 
2289
            (b'd', b'', 0, False, packed_stat),
 
2290
            (b'd', b'', 0, False, packed_stat),
 
2291
            (b'd', b'', 0, False, packed_stat),
2148
2292
            ]
2149
 
        expected_root_entry_direntry = ('', '', 'a-root-value'), [
2150
 
            ('d', '', 0, False, packed_stat),
2151
 
            ('d', '', 0, False, packed_stat),
 
2293
        expected_root_entry_direntry = (b'', b'', b'a-root-value'), [
 
2294
            (b'd', b'', 0, False, packed_stat),
 
2295
            (b'd', b'', 0, False, packed_stat),
2152
2296
            ]
2153
2297
        dirblocks = []
2154
 
        dirblocks.append(('', [root_entry_direntry]))
2155
 
        dirblocks.append(('', []))
 
2298
        dirblocks.append((b'', [root_entry_direntry]))
 
2299
        dirblocks.append((b'', []))
2156
2300
 
2157
2301
        state = self.create_empty_dirstate()
2158
2302
        self.addCleanup(state.unlock)
2159
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2303
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2160
2304
        state._validate()
2161
2305
 
2162
2306
        # This should strip of the extra column
2163
2307
        state._discard_merge_parents()
2164
2308
        state._validate()
2165
 
        expected_dirblocks = [('', [expected_root_entry_direntry]), ('', [])]
 
2309
        expected_dirblocks = [(b'', [expected_root_entry_direntry]), (b'', [])]
2166
2310
        self.assertEqual(expected_dirblocks, state._dirblocks)
2167
2311
 
2168
2312
    def test_discard_absent(self):
2169
2313
        """If entries are only in a merge, discard should remove the entries"""
2170
2314
        null_stat = dirstate.DirState.NULLSTAT
2171
 
        present_dir = ('d', '', 0, False, null_stat)
2172
 
        present_file = ('f', '', 0, False, null_stat)
 
2315
        present_dir = (b'd', b'', 0, False, null_stat)
 
2316
        present_file = (b'f', b'', 0, False, null_stat)
2173
2317
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2174
 
        root_key = ('', '', 'a-root-value')
2175
 
        file_in_root_key = ('', 'file-in-root', 'a-file-id')
2176
 
        file_in_merged_key = ('', 'file-in-merged', 'b-file-id')
2177
 
        dirblocks = [('', [(root_key, [present_dir, present_dir, present_dir])]),
2178
 
                     ('', [(file_in_merged_key,
2179
 
                            [absent, absent, present_file]),
2180
 
                           (file_in_root_key,
2181
 
                            [present_file, present_file, present_file]),
2182
 
                          ]),
2183
 
                    ]
 
2318
        root_key = (b'', b'', b'a-root-value')
 
2319
        file_in_root_key = (b'', b'file-in-root', b'a-file-id')
 
2320
        file_in_merged_key = (b'', b'file-in-merged', b'b-file-id')
 
2321
        dirblocks = [(b'', [(root_key, [present_dir, present_dir, present_dir])]),
 
2322
                     (b'', [(file_in_merged_key,
 
2323
                             [absent, absent, present_file]),
 
2324
                            (file_in_root_key,
 
2325
                             [present_file, present_file, present_file]),
 
2326
                            ]),
 
2327
                     ]
2184
2328
 
2185
2329
        state = self.create_empty_dirstate()
2186
2330
        self.addCleanup(state.unlock)
2187
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2331
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2188
2332
        state._validate()
2189
2333
 
2190
 
        exp_dirblocks = [('', [(root_key, [present_dir, present_dir])]),
2191
 
                         ('', [(file_in_root_key,
2192
 
                                [present_file, present_file]),
2193
 
                              ]),
2194
 
                        ]
 
2334
        exp_dirblocks = [(b'', [(root_key, [present_dir, present_dir])]),
 
2335
                         (b'', [(file_in_root_key,
 
2336
                                 [present_file, present_file]),
 
2337
                                ]),
 
2338
                         ]
2195
2339
        state._discard_merge_parents()
2196
2340
        state._validate()
2197
2341
        self.assertEqual(exp_dirblocks, state._dirblocks)
2198
2342
 
2199
2343
    def test_discard_renamed(self):
2200
2344
        null_stat = dirstate.DirState.NULLSTAT
2201
 
        present_dir = ('d', '', 0, False, null_stat)
2202
 
        present_file = ('f', '', 0, False, null_stat)
 
2345
        present_dir = (b'd', b'', 0, False, null_stat)
 
2346
        present_file = (b'f', b'', 0, False, null_stat)
2203
2347
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2204
 
        root_key = ('', '', 'a-root-value')
2205
 
        file_in_root_key = ('', 'file-in-root', 'a-file-id')
 
2348
        root_key = (b'', b'', b'a-root-value')
 
2349
        file_in_root_key = (b'', b'file-in-root', b'a-file-id')
2206
2350
        # Renamed relative to parent
2207
 
        file_rename_s_key = ('', 'file-s', 'b-file-id')
2208
 
        file_rename_t_key = ('', 'file-t', 'b-file-id')
 
2351
        file_rename_s_key = (b'', b'file-s', b'b-file-id')
 
2352
        file_rename_t_key = (b'', b'file-t', b'b-file-id')
2209
2353
        # And one that is renamed between the parents, but absent in this
2210
 
        key_in_1 = ('', 'file-in-1', 'c-file-id')
2211
 
        key_in_2 = ('', 'file-in-2', 'c-file-id')
 
2354
        key_in_1 = (b'', b'file-in-1', b'c-file-id')
 
2355
        key_in_2 = (b'', b'file-in-2', b'c-file-id')
2212
2356
 
2213
2357
        dirblocks = [
2214
 
            ('', [(root_key, [present_dir, present_dir, present_dir])]),
2215
 
            ('', [(key_in_1,
2216
 
                   [absent, present_file, ('r', 'file-in-2', 'c-file-id')]),
2217
 
                  (key_in_2,
2218
 
                   [absent, ('r', 'file-in-1', 'c-file-id'), present_file]),
2219
 
                  (file_in_root_key,
2220
 
                   [present_file, present_file, present_file]),
2221
 
                  (file_rename_s_key,
2222
 
                   [('r', 'file-t', 'b-file-id'), absent, present_file]),
2223
 
                  (file_rename_t_key,
2224
 
                   [present_file, absent, ('r', 'file-s', 'b-file-id')]),
2225
 
                 ]),
 
2358
            (b'', [(root_key, [present_dir, present_dir, present_dir])]),
 
2359
            (b'', [(key_in_1,
 
2360
                    [absent, present_file, (b'r', b'file-in-2', b'c-file-id')]),
 
2361
                   (key_in_2,
 
2362
                    [absent, (b'r', b'file-in-1', b'c-file-id'), present_file]),
 
2363
                   (file_in_root_key,
 
2364
                    [present_file, present_file, present_file]),
 
2365
                   (file_rename_s_key,
 
2366
                    [(b'r', b'file-t', b'b-file-id'), absent, present_file]),
 
2367
                   (file_rename_t_key,
 
2368
                    [present_file, absent, (b'r', b'file-s', b'b-file-id')]),
 
2369
                   ]),
2226
2370
        ]
2227
2371
        exp_dirblocks = [
2228
 
            ('', [(root_key, [present_dir, present_dir])]),
2229
 
            ('', [(key_in_1, [absent, present_file]),
2230
 
                  (file_in_root_key, [present_file, present_file]),
2231
 
                  (file_rename_t_key, [present_file, absent]),
2232
 
                 ]),
 
2372
            (b'', [(root_key, [present_dir, present_dir])]),
 
2373
            (b'', [(key_in_1, [absent, present_file]),
 
2374
                   (file_in_root_key, [present_file, present_file]),
 
2375
                   (file_rename_t_key, [present_file, absent]),
 
2376
                   ]),
2233
2377
        ]
2234
2378
        state = self.create_empty_dirstate()
2235
2379
        self.addCleanup(state.unlock)
2236
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2380
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2237
2381
        state._validate()
2238
2382
 
2239
2383
        state._discard_merge_parents()
2242
2386
 
2243
2387
    def test_discard_all_subdir(self):
2244
2388
        null_stat = dirstate.DirState.NULLSTAT
2245
 
        present_dir = ('d', '', 0, False, null_stat)
2246
 
        present_file = ('f', '', 0, False, null_stat)
 
2389
        present_dir = (b'd', b'', 0, False, null_stat)
 
2390
        present_file = (b'f', b'', 0, False, null_stat)
2247
2391
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2248
 
        root_key = ('', '', 'a-root-value')
2249
 
        subdir_key = ('', 'sub', 'dir-id')
2250
 
        child1_key = ('sub', 'child1', 'child1-id')
2251
 
        child2_key = ('sub', 'child2', 'child2-id')
2252
 
        child3_key = ('sub', 'child3', 'child3-id')
 
2392
        root_key = (b'', b'', b'a-root-value')
 
2393
        subdir_key = (b'', b'sub', b'dir-id')
 
2394
        child1_key = (b'sub', b'child1', b'child1-id')
 
2395
        child2_key = (b'sub', b'child2', b'child2-id')
 
2396
        child3_key = (b'sub', b'child3', b'child3-id')
2253
2397
 
2254
2398
        dirblocks = [
2255
 
            ('', [(root_key, [present_dir, present_dir, present_dir])]),
2256
 
            ('', [(subdir_key, [present_dir, present_dir, present_dir])]),
2257
 
            ('sub', [(child1_key, [absent, absent, present_file]),
2258
 
                     (child2_key, [absent, absent, present_file]),
2259
 
                     (child3_key, [absent, absent, present_file]),
2260
 
                    ]),
 
2399
            (b'', [(root_key, [present_dir, present_dir, present_dir])]),
 
2400
            (b'', [(subdir_key, [present_dir, present_dir, present_dir])]),
 
2401
            (b'sub', [(child1_key, [absent, absent, present_file]),
 
2402
                      (child2_key, [absent, absent, present_file]),
 
2403
                      (child3_key, [absent, absent, present_file]),
 
2404
                      ]),
2261
2405
        ]
2262
2406
        exp_dirblocks = [
2263
 
            ('', [(root_key, [present_dir, present_dir])]),
2264
 
            ('', [(subdir_key, [present_dir, present_dir])]),
2265
 
            ('sub', []),
 
2407
            (b'', [(root_key, [present_dir, present_dir])]),
 
2408
            (b'', [(subdir_key, [present_dir, present_dir])]),
 
2409
            (b'sub', []),
2266
2410
        ]
2267
2411
        state = self.create_empty_dirstate()
2268
2412
        self.addCleanup(state.unlock)
2269
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2413
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2270
2414
        state._validate()
2271
2415
 
2272
2416
        state._discard_merge_parents()
2282
2426
        # details should always allow join() and always be a plain str when
2283
2427
        # finished
2284
2428
        (minikind, fingerprint, size, executable, tree_data) = details
2285
 
        self.assertIsInstance(minikind, str)
2286
 
        self.assertIsInstance(fingerprint, str)
2287
 
        self.assertIsInstance(tree_data, str)
 
2429
        self.assertIsInstance(minikind, bytes)
 
2430
        self.assertIsInstance(fingerprint, bytes)
 
2431
        self.assertIsInstance(tree_data, bytes)
2288
2432
 
2289
2433
    def test_unicode_symlink(self):
2290
 
        inv_entry = inventory.InventoryLink('link-file-id',
 
2434
        inv_entry = inventory.InventoryLink(b'link-file-id',
2291
2435
                                            u'nam\N{Euro Sign}e',
2292
 
                                            'link-parent-id')
2293
 
        inv_entry.revision = 'link-revision-id'
 
2436
                                            b'link-parent-id')
 
2437
        inv_entry.revision = b'link-revision-id'
2294
2438
        target = u'link-targ\N{Euro Sign}t'
2295
2439
        inv_entry.symlink_target = target
2296
 
        self.assertDetails(('l', target.encode('UTF-8'), 0, False,
2297
 
                            'link-revision-id'), inv_entry)
 
2440
        self.assertDetails((b'l', target.encode('UTF-8'), 0, False,
 
2441
                            b'link-revision-id'), inv_entry)
2298
2442
 
2299
2443
 
2300
2444
class TestSHA1Provider(tests.TestCaseInTempDir):
2305
2449
        self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2306
2450
 
2307
2451
    def test_defaultsha1provider_sha1(self):
2308
 
        text = 'test\r\nwith\nall\rpossible line endings\r\n'
 
2452
        text = b'test\r\nwith\nall\rpossible line endings\r\n'
2309
2453
        self.build_tree_contents([('foo', text)])
2310
2454
        expected_sha = osutils.sha_string(text)
2311
2455
        p = dirstate.DefaultSHA1Provider()
2312
2456
        self.assertEqual(expected_sha, p.sha1('foo'))
2313
2457
 
2314
2458
    def test_defaultsha1provider_stat_and_sha1(self):
2315
 
        text = 'test\r\nwith\nall\rpossible line endings\r\n'
 
2459
        text = b'test\r\nwith\nall\rpossible line endings\r\n'
2316
2460
        self.build_tree_contents([('foo', text)])
2317
2461
        expected_sha = osutils.sha_string(text)
2318
2462
        p = dirstate.DefaultSHA1Provider()
2320
2464
        self.assertTrue(len(statvalue) >= 10)
2321
2465
        self.assertEqual(len(text), statvalue.st_size)
2322
2466
        self.assertEqual(expected_sha, sha1)
 
2467
 
 
2468
 
 
2469
class _Repo(object):
 
2470
    """A minimal api to get InventoryRevisionTree to work."""
 
2471
 
 
2472
    def __init__(self):
 
2473
        default_format = controldir.format_registry.make_controldir('default')
 
2474
        self._format = default_format.repository_format
 
2475
 
 
2476
    def lock_read(self):
 
2477
        pass
 
2478
 
 
2479
    def unlock(self):
 
2480
        pass
 
2481
 
 
2482
 
 
2483
class TestUpdateBasisByDelta(tests.TestCase):
 
2484
 
 
2485
    def path_to_ie(self, path, file_id, rev_id, dir_ids):
 
2486
        if path.endswith('/'):
 
2487
            is_dir = True
 
2488
            path = path[:-1]
 
2489
        else:
 
2490
            is_dir = False
 
2491
        dirname, basename = osutils.split(path)
 
2492
        try:
 
2493
            dir_id = dir_ids[dirname]
 
2494
        except KeyError:
 
2495
            dir_id = osutils.basename(dirname).encode('utf-8') + b'-id'
 
2496
        if is_dir:
 
2497
            ie = inventory.InventoryDirectory(file_id, basename, dir_id)
 
2498
            dir_ids[path] = file_id
 
2499
        else:
 
2500
            ie = inventory.InventoryFile(file_id, basename, dir_id)
 
2501
            ie.text_size = 0
 
2502
            ie.text_sha1 = b''
 
2503
        ie.revision = rev_id
 
2504
        return ie
 
2505
 
 
2506
    def create_tree_from_shape(self, rev_id, shape):
 
2507
        dir_ids = {'': b'root-id'}
 
2508
        inv = inventory.Inventory(b'root-id', rev_id)
 
2509
        for info in shape:
 
2510
            if len(info) == 2:
 
2511
                path, file_id = info
 
2512
                ie_rev_id = rev_id
 
2513
            else:
 
2514
                path, file_id, ie_rev_id = info
 
2515
            if path == '':
 
2516
                # Replace the root entry
 
2517
                del inv._byid[inv.root.file_id]
 
2518
                inv.root.file_id = file_id
 
2519
                inv._byid[file_id] = inv.root
 
2520
                dir_ids[''] = file_id
 
2521
                continue
 
2522
            inv.add(self.path_to_ie(path, file_id, ie_rev_id, dir_ids))
 
2523
        return inventorytree.InventoryRevisionTree(_Repo(), inv, rev_id)
 
2524
 
 
2525
    def create_empty_dirstate(self):
 
2526
        fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
 
2527
        self.addCleanup(os.remove, path)
 
2528
        os.close(fd)
 
2529
        state = dirstate.DirState.initialize(path)
 
2530
        self.addCleanup(state.unlock)
 
2531
        return state
 
2532
 
 
2533
    def create_inv_delta(self, delta, rev_id):
 
2534
        """Translate a 'delta shape' into an actual InventoryDelta"""
 
2535
        dir_ids = {'': b'root-id'}
 
2536
        inv_delta = []
 
2537
        for old_path, new_path, file_id in delta:
 
2538
            if old_path is not None and old_path.endswith('/'):
 
2539
                # Don't have to actually do anything for this, because only
 
2540
                # new_path creates InventoryEntries
 
2541
                old_path = old_path[:-1]
 
2542
            if new_path is None:  # Delete
 
2543
                inv_delta.append((old_path, None, file_id, None))
 
2544
                continue
 
2545
            ie = self.path_to_ie(new_path, file_id, rev_id, dir_ids)
 
2546
            inv_delta.append((old_path, new_path, file_id, ie))
 
2547
        return inv_delta
 
2548
 
 
2549
    def assertUpdate(self, active, basis, target):
 
2550
        """Assert that update_basis_by_delta works how we want.
 
2551
 
 
2552
        Set up a DirState object with active_shape for tree 0, basis_shape for
 
2553
        tree 1. Then apply the delta from basis_shape to target_shape,
 
2554
        and assert that the DirState is still valid, and that its stored
 
2555
        content matches the target_shape.
 
2556
        """
 
2557
        active_tree = self.create_tree_from_shape(b'active', active)
 
2558
        basis_tree = self.create_tree_from_shape(b'basis', basis)
 
2559
        target_tree = self.create_tree_from_shape(b'target', target)
 
2560
        state = self.create_empty_dirstate()
 
2561
        state.set_state_from_scratch(active_tree.root_inventory,
 
2562
                                     [(b'basis', basis_tree)], [])
 
2563
        delta = target_tree.root_inventory._make_delta(
 
2564
            basis_tree.root_inventory)
 
2565
        state.update_basis_by_delta(delta, b'target')
 
2566
        state._validate()
 
2567
        dirstate_tree = workingtree_4.DirStateRevisionTree(
 
2568
            state, b'target', _Repo(), None)
 
2569
        # The target now that delta has been applied should match the
 
2570
        # RevisionTree
 
2571
        self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
 
2572
        # And the dirblock state should be identical to the state if we created
 
2573
        # it from scratch.
 
2574
        state2 = self.create_empty_dirstate()
 
2575
        state2.set_state_from_scratch(active_tree.root_inventory,
 
2576
                                      [(b'target', target_tree)], [])
 
2577
        self.assertEqual(state2._dirblocks, state._dirblocks)
 
2578
        return state
 
2579
 
 
2580
    def assertBadDelta(self, active, basis, delta):
 
2581
        """Test that we raise InconsistentDelta when appropriate.
 
2582
 
 
2583
        :param active: The active tree shape
 
2584
        :param basis: The basis tree shape
 
2585
        :param delta: A description of the delta to apply. Similar to the form
 
2586
            for regular inventory deltas, but omitting the InventoryEntry.
 
2587
            So adding a file is: (None, 'path', b'file-id')
 
2588
            Adding a directory is: (None, 'path/', b'dir-id')
 
2589
            Renaming a dir is: ('old/', 'new/', b'dir-id')
 
2590
            etc.
 
2591
        """
 
2592
        active_tree = self.create_tree_from_shape(b'active', active)
 
2593
        basis_tree = self.create_tree_from_shape(b'basis', basis)
 
2594
        inv_delta = self.create_inv_delta(delta, b'target')
 
2595
        state = self.create_empty_dirstate()
 
2596
        state.set_state_from_scratch(active_tree.root_inventory,
 
2597
                                     [(b'basis', basis_tree)], [])
 
2598
        self.assertRaises(errors.InconsistentDelta,
 
2599
                          state.update_basis_by_delta, inv_delta, b'target')
 
2600
        # try:
 
2601
        ##     state.update_basis_by_delta(inv_delta, b'target')
 
2602
        # except errors.InconsistentDelta, e:
 
2603
        ##     import pdb; pdb.set_trace()
 
2604
        # else:
 
2605
        ##     import pdb; pdb.set_trace()
 
2606
        self.assertTrue(state._changes_aborted)
 
2607
 
 
2608
    def test_remove_file_matching_active_state(self):
 
2609
        state = self.assertUpdate(
 
2610
            active=[],
 
2611
            basis=[('file', b'file-id')],
 
2612
            target=[],
 
2613
            )
 
2614
 
 
2615
    def test_remove_file_present_in_active_state(self):
 
2616
        state = self.assertUpdate(
 
2617
            active=[('file', b'file-id')],
 
2618
            basis=[('file', b'file-id')],
 
2619
            target=[],
 
2620
            )
 
2621
 
 
2622
    def test_remove_file_present_elsewhere_in_active_state(self):
 
2623
        state = self.assertUpdate(
 
2624
            active=[('other-file', b'file-id')],
 
2625
            basis=[('file', b'file-id')],
 
2626
            target=[],
 
2627
            )
 
2628
 
 
2629
    def test_remove_file_active_state_has_diff_file(self):
 
2630
        state = self.assertUpdate(
 
2631
            active=[('file', b'file-id-2')],
 
2632
            basis=[('file', b'file-id')],
 
2633
            target=[],
 
2634
            )
 
2635
 
 
2636
    def test_remove_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2637
        state = self.assertUpdate(
 
2638
            active=[('file', b'file-id-2'),
 
2639
                    ('other-file', b'file-id')],
 
2640
            basis=[('file', b'file-id')],
 
2641
            target=[],
 
2642
            )
 
2643
 
 
2644
    def test_add_file_matching_active_state(self):
 
2645
        state = self.assertUpdate(
 
2646
            active=[('file', b'file-id')],
 
2647
            basis=[],
 
2648
            target=[('file', b'file-id')],
 
2649
            )
 
2650
 
 
2651
    def test_add_file_in_empty_dir_not_matching_active_state(self):
 
2652
        state = self.assertUpdate(
 
2653
            active=[],
 
2654
            basis=[('dir/', b'dir-id')],
 
2655
            target=[('dir/', b'dir-id', b'basis'), ('dir/file', b'file-id')],
 
2656
            )
 
2657
 
 
2658
    def test_add_file_missing_in_active_state(self):
 
2659
        state = self.assertUpdate(
 
2660
            active=[],
 
2661
            basis=[],
 
2662
            target=[('file', b'file-id')],
 
2663
            )
 
2664
 
 
2665
    def test_add_file_elsewhere_in_active_state(self):
 
2666
        state = self.assertUpdate(
 
2667
            active=[('other-file', b'file-id')],
 
2668
            basis=[],
 
2669
            target=[('file', b'file-id')],
 
2670
            )
 
2671
 
 
2672
    def test_add_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2673
        state = self.assertUpdate(
 
2674
            active=[('other-file', b'file-id'),
 
2675
                    ('file', b'file-id-2')],
 
2676
            basis=[],
 
2677
            target=[('file', b'file-id')],
 
2678
            )
 
2679
 
 
2680
    def test_rename_file_matching_active_state(self):
 
2681
        state = self.assertUpdate(
 
2682
            active=[('other-file', b'file-id')],
 
2683
            basis=[('file', b'file-id')],
 
2684
            target=[('other-file', b'file-id')],
 
2685
            )
 
2686
 
 
2687
    def test_rename_file_missing_in_active_state(self):
 
2688
        state = self.assertUpdate(
 
2689
            active=[],
 
2690
            basis=[('file', b'file-id')],
 
2691
            target=[('other-file', b'file-id')],
 
2692
            )
 
2693
 
 
2694
    def test_rename_file_present_elsewhere_in_active_state(self):
 
2695
        state = self.assertUpdate(
 
2696
            active=[('third', b'file-id')],
 
2697
            basis=[('file', b'file-id')],
 
2698
            target=[('other-file', b'file-id')],
 
2699
            )
 
2700
 
 
2701
    def test_rename_file_active_state_has_diff_source_file(self):
 
2702
        state = self.assertUpdate(
 
2703
            active=[('file', b'file-id-2')],
 
2704
            basis=[('file', b'file-id')],
 
2705
            target=[('other-file', b'file-id')],
 
2706
            )
 
2707
 
 
2708
    def test_rename_file_active_state_has_diff_target_file(self):
 
2709
        state = self.assertUpdate(
 
2710
            active=[('other-file', b'file-id-2')],
 
2711
            basis=[('file', b'file-id')],
 
2712
            target=[('other-file', b'file-id')],
 
2713
            )
 
2714
 
 
2715
    def test_rename_file_active_has_swapped_files(self):
 
2716
        state = self.assertUpdate(
 
2717
            active=[('file', b'file-id'),
 
2718
                    ('other-file', b'file-id-2')],
 
2719
            basis=[('file', b'file-id'),
 
2720
                   ('other-file', b'file-id-2')],
 
2721
            target=[('file', b'file-id-2'),
 
2722
                    ('other-file', b'file-id')])
 
2723
 
 
2724
    def test_rename_file_basis_has_swapped_files(self):
 
2725
        state = self.assertUpdate(
 
2726
            active=[('file', b'file-id'),
 
2727
                    ('other-file', b'file-id-2')],
 
2728
            basis=[('file', b'file-id-2'),
 
2729
                   ('other-file', b'file-id')],
 
2730
            target=[('file', b'file-id'),
 
2731
                    ('other-file', b'file-id-2')])
 
2732
 
 
2733
    def test_rename_directory_with_contents(self):
 
2734
        state = self.assertUpdate(  # active matches basis
 
2735
            active=[('dir1/', b'dir-id'),
 
2736
                    ('dir1/file', b'file-id')],
 
2737
            basis=[('dir1/', b'dir-id'),
 
2738
                   ('dir1/file', b'file-id')],
 
2739
            target=[('dir2/', b'dir-id'),
 
2740
                    ('dir2/file', b'file-id')])
 
2741
        state = self.assertUpdate(  # active matches target
 
2742
            active=[('dir2/', b'dir-id'),
 
2743
                    ('dir2/file', b'file-id')],
 
2744
            basis=[('dir1/', b'dir-id'),
 
2745
                   ('dir1/file', b'file-id')],
 
2746
            target=[('dir2/', b'dir-id'),
 
2747
                    ('dir2/file', b'file-id')])
 
2748
        state = self.assertUpdate(  # active empty
 
2749
            active=[],
 
2750
            basis=[('dir1/', b'dir-id'),
 
2751
                   ('dir1/file', b'file-id')],
 
2752
            target=[('dir2/', b'dir-id'),
 
2753
                    ('dir2/file', b'file-id')])
 
2754
        state = self.assertUpdate(  # active present at other location
 
2755
            active=[('dir3/', b'dir-id'),
 
2756
                    ('dir3/file', b'file-id')],
 
2757
            basis=[('dir1/', b'dir-id'),
 
2758
                   ('dir1/file', b'file-id')],
 
2759
            target=[('dir2/', b'dir-id'),
 
2760
                    ('dir2/file', b'file-id')])
 
2761
        state = self.assertUpdate(  # active has different ids
 
2762
            active=[('dir1/', b'dir1-id'),
 
2763
                    ('dir1/file', b'file1-id'),
 
2764
                    ('dir2/', b'dir2-id'),
 
2765
                    ('dir2/file', b'file2-id')],
 
2766
            basis=[('dir1/', b'dir-id'),
 
2767
                   ('dir1/file', b'file-id')],
 
2768
            target=[('dir2/', b'dir-id'),
 
2769
                    ('dir2/file', b'file-id')])
 
2770
 
 
2771
    def test_invalid_file_not_present(self):
 
2772
        state = self.assertBadDelta(
 
2773
            active=[('file', b'file-id')],
 
2774
            basis=[('file', b'file-id')],
 
2775
            delta=[('other-file', 'file', b'file-id')])
 
2776
 
 
2777
    def test_invalid_new_id_same_path(self):
 
2778
        # The bad entry comes after
 
2779
        state = self.assertBadDelta(
 
2780
            active=[('file', b'file-id')],
 
2781
            basis=[('file', b'file-id')],
 
2782
            delta=[(None, 'file', b'file-id-2')])
 
2783
        # The bad entry comes first
 
2784
        state = self.assertBadDelta(
 
2785
            active=[('file', b'file-id-2')],
 
2786
            basis=[('file', b'file-id-2')],
 
2787
            delta=[(None, 'file', b'file-id')])
 
2788
 
 
2789
    def test_invalid_existing_id(self):
 
2790
        state = self.assertBadDelta(
 
2791
            active=[('file', b'file-id')],
 
2792
            basis=[('file', b'file-id')],
 
2793
            delta=[(None, 'file', b'file-id')])
 
2794
 
 
2795
    def test_invalid_parent_missing(self):
 
2796
        state = self.assertBadDelta(
 
2797
            active=[],
 
2798
            basis=[],
 
2799
            delta=[(None, 'path/path2', b'file-id')])
 
2800
        # Note: we force the active tree to have the directory, by knowing how
 
2801
        #       path_to_ie handles entries with missing parents
 
2802
        state = self.assertBadDelta(
 
2803
            active=[('path/', b'path-id')],
 
2804
            basis=[],
 
2805
            delta=[(None, 'path/path2', b'file-id')])
 
2806
        state = self.assertBadDelta(
 
2807
            active=[('path/', b'path-id'),
 
2808
                    ('path/path2', b'file-id')],
 
2809
            basis=[],
 
2810
            delta=[(None, 'path/path2', b'file-id')])
 
2811
 
 
2812
    def test_renamed_dir_same_path(self):
 
2813
        # We replace the parent directory, with another parent dir. But the C
 
2814
        # file doesn't look like it has been moved.
 
2815
        state = self.assertUpdate(  # Same as basis
 
2816
            active=[('dir/', b'A-id'),
 
2817
                    ('dir/B', b'B-id')],
 
2818
            basis=[('dir/', b'A-id'),
 
2819
                   ('dir/B', b'B-id')],
 
2820
            target=[('dir/', b'C-id'),
 
2821
                    ('dir/B', b'B-id')])
 
2822
        state = self.assertUpdate(  # Same as target
 
2823
            active=[('dir/', b'C-id'),
 
2824
                    ('dir/B', b'B-id')],
 
2825
            basis=[('dir/', b'A-id'),
 
2826
                   ('dir/B', b'B-id')],
 
2827
            target=[('dir/', b'C-id'),
 
2828
                    ('dir/B', b'B-id')])
 
2829
        state = self.assertUpdate(  # empty active
 
2830
            active=[],
 
2831
            basis=[('dir/', b'A-id'),
 
2832
                   ('dir/B', b'B-id')],
 
2833
            target=[('dir/', b'C-id'),
 
2834
                    ('dir/B', b'B-id')])
 
2835
        state = self.assertUpdate(  # different active
 
2836
            active=[('dir/', b'D-id'),
 
2837
                    ('dir/B', b'B-id')],
 
2838
            basis=[('dir/', b'A-id'),
 
2839
                   ('dir/B', b'B-id')],
 
2840
            target=[('dir/', b'C-id'),
 
2841
                    ('dir/B', b'B-id')])
 
2842
 
 
2843
    def test_parent_child_swap(self):
 
2844
        state = self.assertUpdate(  # Same as basis
 
2845
            active=[('A/', b'A-id'),
 
2846
                    ('A/B/', b'B-id'),
 
2847
                    ('A/B/C', b'C-id')],
 
2848
            basis=[('A/', b'A-id'),
 
2849
                   ('A/B/', b'B-id'),
 
2850
                   ('A/B/C', b'C-id')],
 
2851
            target=[('A/', b'B-id'),
 
2852
                    ('A/B/', b'A-id'),
 
2853
                    ('A/B/C', b'C-id')])
 
2854
        state = self.assertUpdate(  # Same as target
 
2855
            active=[('A/', b'B-id'),
 
2856
                    ('A/B/', b'A-id'),
 
2857
                    ('A/B/C', b'C-id')],
 
2858
            basis=[('A/', b'A-id'),
 
2859
                   ('A/B/', b'B-id'),
 
2860
                   ('A/B/C', b'C-id')],
 
2861
            target=[('A/', b'B-id'),
 
2862
                    ('A/B/', b'A-id'),
 
2863
                    ('A/B/C', b'C-id')])
 
2864
        state = self.assertUpdate(  # empty active
 
2865
            active=[],
 
2866
            basis=[('A/', b'A-id'),
 
2867
                   ('A/B/', b'B-id'),
 
2868
                   ('A/B/C', b'C-id')],
 
2869
            target=[('A/', b'B-id'),
 
2870
                    ('A/B/', b'A-id'),
 
2871
                    ('A/B/C', b'C-id')])
 
2872
        state = self.assertUpdate(  # different active
 
2873
            active=[('D/', b'A-id'),
 
2874
                    ('D/E/', b'B-id'),
 
2875
                    ('F', b'C-id')],
 
2876
            basis=[('A/', b'A-id'),
 
2877
                   ('A/B/', b'B-id'),
 
2878
                   ('A/B/C', b'C-id')],
 
2879
            target=[('A/', b'B-id'),
 
2880
                    ('A/B/', b'A-id'),
 
2881
                    ('A/B/C', b'C-id')])
 
2882
 
 
2883
    def test_change_root_id(self):
 
2884
        state = self.assertUpdate(  # same as basis
 
2885
            active=[('', b'root-id'),
 
2886
                    ('file', b'file-id')],
 
2887
            basis=[('', b'root-id'),
 
2888
                   ('file', b'file-id')],
 
2889
            target=[('', b'target-root-id'),
 
2890
                    ('file', b'file-id')])
 
2891
        state = self.assertUpdate(  # same as target
 
2892
            active=[('', b'target-root-id'),
 
2893
                    ('file', b'file-id')],
 
2894
            basis=[('', b'root-id'),
 
2895
                   ('file', b'file-id')],
 
2896
            target=[('', b'target-root-id'),
 
2897
                    ('file', b'root-id')])
 
2898
        state = self.assertUpdate(  # all different
 
2899
            active=[('', b'active-root-id'),
 
2900
                    ('file', b'file-id')],
 
2901
            basis=[('', b'root-id'),
 
2902
                   ('file', b'file-id')],
 
2903
            target=[('', b'target-root-id'),
 
2904
                    ('file', b'root-id')])
 
2905
 
 
2906
    def test_change_file_absent_in_active(self):
 
2907
        state = self.assertUpdate(
 
2908
            active=[],
 
2909
            basis=[('file', b'file-id')],
 
2910
            target=[('file', b'file-id')])
 
2911
 
 
2912
    def test_invalid_changed_file(self):
 
2913
        state = self.assertBadDelta(  # Not present in basis
 
2914
            active=[('file', b'file-id')],
 
2915
            basis=[],
 
2916
            delta=[('file', 'file', b'file-id')])
 
2917
        state = self.assertBadDelta(  # present at another location in basis
 
2918
            active=[('file', b'file-id')],
 
2919
            basis=[('other-file', b'file-id')],
 
2920
            delta=[('file', 'file', b'file-id')])