/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: 2018-11-18 18:23:32 UTC
  • mto: This revision was merged to the branch mainline in revision 7197.
  • Revision ID: jelmer@jelmer.uk-20181118182332-viz1qvqese2mo9i6
Fix some more Bazaar references.

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.get_root_id()),  # 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.get_root_id()),  # 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.get_root_id()),  # 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.get_root_id()),  # 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.get_root_id()),  # 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.get_root_id()),  # 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.get_root_id()),  # 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.get_root_id()),  # 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')
 
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')
833
902
            root_id = tree1.get_root_id()
834
 
            inv = tree1.inventory
 
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:
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
1119
            root_id = tree2.get_root_id()
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
1263
1339
        self.build_tree(['tree1/b'])
1264
1340
        tree1.lock_write()
1265
1341
        try:
1266
 
            tree1.add(['b'], ['b-id'])
 
1342
            tree1.add(['b'], [b'b-id'])
1267
1343
            root_id = tree1.get_root_id()
1268
 
            inv = tree1.inventory
 
1344
            inv = tree1.root_inventory
1269
1345
            state = dirstate.DirState.initialize('dirstate')
1270
1346
            try:
1271
1347
                # Set the initial state with 'b'
1272
1348
                state.set_state_from_inventory(inv)
1273
 
                inv.rename('b-id', root_id, 'a')
 
1349
                inv.rename(b'b-id', root_id, 'a')
1274
1350
                # Set the new state with 'a', which currently corrupts.
1275
1351
                state.set_state_from_inventory(inv)
1276
 
                expected_result1 = [('', '', root_id, 'd'),
1277
 
                                    ('', 'a', 'b-id', 'f'),
1278
 
                                   ]
 
1352
                expected_result1 = [(b'', b'', root_id, b'd'),
 
1353
                                    (b'', b'a', b'b-id', b'f'),
 
1354
                                    ]
1279
1355
                values = []
1280
1356
                for entry in state._iter_entries():
1281
1357
                    values.append(entry[0] + entry[1][0][:1])
1286
1362
            tree1.unlock()
1287
1363
 
1288
1364
 
 
1365
class TestDirStateHashUpdates(TestCaseWithDirState):
 
1366
 
 
1367
    def do_update_entry(self, state, path):
 
1368
        entry = state._get_entry(0, path_utf8=path)
 
1369
        stat = os.lstat(path)
 
1370
        return dirstate.update_entry(state, entry, os.path.abspath(path), stat)
 
1371
 
 
1372
    def _read_state_content(self, state):
 
1373
        """Read the content of the dirstate file.
 
1374
 
 
1375
        On Windows when one process locks a file, you can't even open() the
 
1376
        file in another process (to read it). So we go directly to
 
1377
        state._state_file. This should always be the exact disk representation,
 
1378
        so it is reasonable to do so.
 
1379
        DirState also always seeks before reading, so it doesn't matter if we
 
1380
        bump the file pointer.
 
1381
        """
 
1382
        state._state_file.seek(0)
 
1383
        return state._state_file.read()
 
1384
 
 
1385
    def test_worth_saving_limit_avoids_writing(self):
 
1386
        tree = self.make_branch_and_tree('.')
 
1387
        self.build_tree(['c', 'd'])
 
1388
        tree.lock_write()
 
1389
        tree.add(['c', 'd'], [b'c-id', b'd-id'])
 
1390
        tree.commit('add c and d')
 
1391
        state = InstrumentedDirState.on_file(tree.current_dirstate()._filename,
 
1392
                                             worth_saving_limit=2)
 
1393
        tree.unlock()
 
1394
        state.lock_write()
 
1395
        self.addCleanup(state.unlock)
 
1396
        state._read_dirblocks_if_needed()
 
1397
        state.adjust_time(+20)  # Allow things to be cached
 
1398
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1399
                         state._dirblock_state)
 
1400
        content = self._read_state_content(state)
 
1401
        self.do_update_entry(state, b'c')
 
1402
        self.assertEqual(1, len(state._known_hash_changes))
 
1403
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1404
                         state._dirblock_state)
 
1405
        state.save()
 
1406
        # It should not have set the state to IN_MEMORY_UNMODIFIED because the
 
1407
        # hash values haven't been written out.
 
1408
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1409
                         state._dirblock_state)
 
1410
        self.assertEqual(content, self._read_state_content(state))
 
1411
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1412
                         state._dirblock_state)
 
1413
        self.do_update_entry(state, b'd')
 
1414
        self.assertEqual(2, len(state._known_hash_changes))
 
1415
        state.save()
 
1416
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1417
                         state._dirblock_state)
 
1418
        self.assertEqual(0, len(state._known_hash_changes))
 
1419
 
 
1420
 
1289
1421
class TestGetLines(TestCaseWithDirState):
1290
1422
 
1291
1423
    def test_get_line_with_2_rows(self):
1292
1424
        state = self.create_dirstate_with_root_and_subdir()
1293
1425
        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())
 
1426
            self.assertEqual([b'#bazaar dirstate flat format 3\n',
 
1427
                              b'crc32: 41262208\n',
 
1428
                              b'num_entries: 2\n',
 
1429
                              b'0\x00\n\x00'
 
1430
                              b'0\x00\n\x00'
 
1431
                              b'\x00\x00a-root-value\x00'
 
1432
                              b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
 
1433
                              b'\x00subdir\x00subdir-id\x00'
 
1434
                              b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
 
1435
                              ], state.get_lines())
1304
1436
        finally:
1305
1437
            state.unlock()
1306
1438
 
1308
1440
        state = self.create_dirstate_with_root()
1309
1441
        try:
1310
1442
            self.assertEqual(
1311
 
                '\x00\x00a-root-value\x00d\x00\x000\x00n'
1312
 
                '\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk',
 
1443
                b'\x00\x00a-root-value\x00d\x00\x000\x00n'
 
1444
                b'\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk',
1313
1445
                state._entry_to_line(state._dirblocks[0][1][0]))
1314
1446
        finally:
1315
1447
            state.unlock()
1316
1448
 
1317
1449
    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, ''),
 
1450
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1451
        root_entry = (b'', b'', b'a-root-value'), [
 
1452
            (b'd', b'', 0, False, packed_stat),  # current tree details
 
1453
            # first: a pointer to the current location
 
1454
            (b'a', b'dirname/basename', 0, False, b''),
1323
1455
            ]
1324
1456
        state = dirstate.DirState.initialize('dirstate')
1325
1457
        try:
1326
1458
            self.assertEqual(
1327
 
                '\x00\x00a-root-value\x00'
1328
 
                'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
1329
 
                'a\x00dirname/basename\x000\x00n\x00',
 
1459
                b'\x00\x00a-root-value\x00'
 
1460
                b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
1461
                b'a\x00dirname/basename\x000\x00n\x00',
1330
1462
                state._entry_to_line(root_entry))
1331
1463
        finally:
1332
1464
            state.unlock()
1333
1465
 
1334
1466
    def test_entry_to_line_with_two_parents_at_different_paths(self):
1335
1467
        # / 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, ''),
 
1468
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1469
        root_entry = (b'', b'', b'a-root-value'), [
 
1470
            (b'd', b'', 0, False, packed_stat),  # current tree details
 
1471
            (b'd', b'', 0, False, b'rev_id'),  # first parent details
 
1472
            # second: a pointer to the current location
 
1473
            (b'a', b'dirname/basename', 0, False, b''),
1342
1474
            ]
1343
1475
        state = dirstate.DirState.initialize('dirstate')
1344
1476
        try:
1345
1477
            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',
 
1478
                b'\x00\x00a-root-value\x00'
 
1479
                b'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
1480
                b'd\x00\x000\x00n\x00rev_id\x00'
 
1481
                b'a\x00dirname/basename\x000\x00n\x00',
1350
1482
                state._entry_to_line(root_entry))
1351
1483
        finally:
1352
1484
            state.unlock()
1354
1486
    def test_iter_entries(self):
1355
1487
        # we should be able to iterate the dirstate entries from end to end
1356
1488
        # this is for get_lines to be easy to read.
1357
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1489
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1358
1490
        dirblocks = []
1359
 
        root_entries = [(('', '', 'a-root-value'), [
1360
 
            ('d', '', 0, False, packed_stat), # current tree details
 
1491
        root_entries = [((b'', b'', b'a-root-value'), [
 
1492
            (b'd', b'', 0, False, packed_stat),  # current tree details
1361
1493
            ])]
1362
1494
        dirblocks.append(('', root_entries))
1363
1495
        # add two files in the root
1364
 
        subdir_entry = ('', 'subdir', 'subdir-id'), [
1365
 
            ('d', '', 0, False, packed_stat), # current tree details
 
1496
        subdir_entry = (b'', b'subdir', b'subdir-id'), [
 
1497
            (b'd', b'', 0, False, packed_stat),  # current tree details
1366
1498
            ]
1367
 
        afile_entry = ('', 'afile', 'afile-id'), [
1368
 
            ('f', 'sha1value', 34, False, packed_stat), # current tree details
 
1499
        afile_entry = (b'', b'afile', b'afile-id'), [
 
1500
            (b'f', b'sha1value', 34, False, packed_stat),  # current tree details
1369
1501
            ]
1370
1502
        dirblocks.append(('', [subdir_entry, afile_entry]))
1371
1503
        # and one in subdir
1372
 
        file_entry2 = ('subdir', '2file', '2file-id'), [
1373
 
            ('f', 'sha1value', 23, False, packed_stat), # current tree details
 
1504
        file_entry2 = (b'subdir', b'2file', b'2file-id'), [
 
1505
            (b'f', b'sha1value', 23, False, packed_stat),  # current tree details
1374
1506
            ]
1375
1507
        dirblocks.append(('subdir', [file_entry2]))
1376
1508
        state = dirstate.DirState.initialize('dirstate')
1386
1518
class TestGetBlockRowIndex(TestCaseWithDirState):
1387
1519
 
1388
1520
    def assertBlockRowIndexEqual(self, block_index, row_index, dir_present,
1389
 
        file_present, state, dirname, basename, tree_index):
 
1521
                                 file_present, state, dirname, basename, tree_index):
1390
1522
        self.assertEqual((block_index, row_index, dir_present, file_present),
1391
 
            state._get_block_entry_index(dirname, basename, tree_index))
 
1523
                         state._get_block_entry_index(dirname, basename, tree_index))
1392
1524
        if dir_present:
1393
1525
            block = state._dirblocks[block_index]
1394
1526
            self.assertEqual(dirname, block[0])
1400
1532
    def test_simple_structure(self):
1401
1533
        state = self.create_dirstate_with_root_and_subdir()
1402
1534
        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)
 
1535
        self.assertBlockRowIndexEqual(
 
1536
            1, 0, True, True, state, b'', b'subdir', 0)
 
1537
        self.assertBlockRowIndexEqual(
 
1538
            1, 0, True, False, state, b'', b'bdir', 0)
 
1539
        self.assertBlockRowIndexEqual(
 
1540
            1, 1, True, False, state, b'', b'zdir', 0)
 
1541
        self.assertBlockRowIndexEqual(
 
1542
            2, 0, False, False, state, b'a', b'foo', 0)
1407
1543
        self.assertBlockRowIndexEqual(2, 0, False, False, state,
1408
 
                                      'subdir', 'foo', 0)
 
1544
                                      b'subdir', b'foo', 0)
1409
1545
 
1410
1546
    def test_complex_structure_exists(self):
1411
1547
        state = self.create_complex_dirstate()
1412
1548
        self.addCleanup(state.unlock)
1413
1549
        # 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)
 
1550
        self.assertBlockRowIndexEqual(0, 0, True, True, state, b'', b'', 0)
 
1551
        self.assertBlockRowIndexEqual(1, 0, True, True, state, b'', b'a', 0)
 
1552
        self.assertBlockRowIndexEqual(1, 1, True, True, state, b'', b'b', 0)
 
1553
        self.assertBlockRowIndexEqual(1, 2, True, True, state, b'', b'c', 0)
 
1554
        self.assertBlockRowIndexEqual(1, 3, True, True, state, b'', b'd', 0)
 
1555
        self.assertBlockRowIndexEqual(2, 0, True, True, state, b'a', b'e', 0)
 
1556
        self.assertBlockRowIndexEqual(2, 1, True, True, state, b'a', b'f', 0)
 
1557
        self.assertBlockRowIndexEqual(3, 0, True, True, state, b'b', b'g', 0)
1422
1558
        self.assertBlockRowIndexEqual(3, 1, True, True, state,
1423
 
                                      'b', 'h\xc3\xa5', 0)
 
1559
                                      b'b', b'h\xc3\xa5', 0)
1424
1560
 
1425
1561
    def test_complex_structure_missing(self):
1426
1562
        state = self.create_complex_dirstate()
1427
1563
        self.addCleanup(state.unlock)
1428
1564
        # Make sure things would be inserted in the right locations
1429
1565
        # '_' 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)
 
1566
        self.assertBlockRowIndexEqual(0, 0, True, True, state, b'', b'', 0)
 
1567
        self.assertBlockRowIndexEqual(1, 0, True, False, state, b'', b'_', 0)
 
1568
        self.assertBlockRowIndexEqual(1, 1, True, False, state, b'', b'aa', 0)
1433
1569
        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)
 
1570
                                      b'', b'h\xc3\xa5', 0)
 
1571
        self.assertBlockRowIndexEqual(2, 0, False, False, state, b'_', b'a', 0)
 
1572
        self.assertBlockRowIndexEqual(
 
1573
            3, 0, False, False, state, b'aa', b'a', 0)
 
1574
        self.assertBlockRowIndexEqual(
 
1575
            4, 0, False, False, state, b'bb', b'a', 0)
1438
1576
        # This would be inserted between a/ and b/
1439
 
        self.assertBlockRowIndexEqual(3, 0, False, False, state, 'a/e', 'a', 0)
 
1577
        self.assertBlockRowIndexEqual(
 
1578
            3, 0, False, False, state, b'a/e', b'a', 0)
1440
1579
        # Put at the end
1441
 
        self.assertBlockRowIndexEqual(4, 0, False, False, state, 'e', 'a', 0)
 
1580
        self.assertBlockRowIndexEqual(4, 0, False, False, state, b'e', b'a', 0)
1442
1581
 
1443
1582
 
1444
1583
class TestGetEntry(TestCaseWithDirState):
1455
1594
    def test_simple_structure(self):
1456
1595
        state = self.create_dirstate_with_root_and_subdir()
1457
1596
        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)
 
1597
        self.assertEntryEqual(b'', b'', b'a-root-value', state, b'', 0)
 
1598
        self.assertEntryEqual(
 
1599
            b'', b'subdir', b'subdir-id', state, b'subdir', 0)
 
1600
        self.assertEntryEqual(None, None, None, state, b'missing', 0)
 
1601
        self.assertEntryEqual(None, None, None, state, b'missing/foo', 0)
 
1602
        self.assertEntryEqual(None, None, None, state, b'subdir/foo', 0)
1463
1603
 
1464
1604
    def test_complex_structure_exists(self):
1465
1605
        state = self.create_complex_dirstate()
1466
1606
        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)
 
1607
        self.assertEntryEqual(b'', b'', b'a-root-value', state, b'', 0)
 
1608
        self.assertEntryEqual(b'', b'a', b'a-dir', state, b'a', 0)
 
1609
        self.assertEntryEqual(b'', b'b', b'b-dir', state, b'b', 0)
 
1610
        self.assertEntryEqual(b'', b'c', b'c-file', state, b'c', 0)
 
1611
        self.assertEntryEqual(b'', b'd', b'd-file', state, b'd', 0)
 
1612
        self.assertEntryEqual(b'a', b'e', b'e-dir', state, b'a/e', 0)
 
1613
        self.assertEntryEqual(b'a', b'f', b'f-file', state, b'a/f', 0)
 
1614
        self.assertEntryEqual(b'b', b'g', b'g-file', state, b'b/g', 0)
 
1615
        self.assertEntryEqual(b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file', state,
 
1616
                              b'b/h\xc3\xa5', 0)
1477
1617
 
1478
1618
    def test_complex_structure_missing(self):
1479
1619
        state = self.create_complex_dirstate()
1480
1620
        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)
 
1621
        self.assertEntryEqual(None, None, None, state, b'_', 0)
 
1622
        self.assertEntryEqual(None, None, None, state, b'_\xc3\xa5', 0)
 
1623
        self.assertEntryEqual(None, None, None, state, b'a/b', 0)
 
1624
        self.assertEntryEqual(None, None, None, state, b'c/d', 0)
1485
1625
 
1486
1626
    def test_get_entry_uninitialized(self):
1487
1627
        """Calling get_entry will load data if it needs to"""
1498
1638
                             state._header_state)
1499
1639
            self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
1500
1640
                             state._dirblock_state)
1501
 
            self.assertEntryEqual('', '', 'a-root-value', state, '', 0)
 
1641
            self.assertEntryEqual(b'', b'', b'a-root-value', state, b'', 0)
1502
1642
        finally:
1503
1643
            state.unlock()
1504
1644
 
1528
1668
 
1529
1669
        :return: The dirstate, still write-locked.
1530
1670
        """
1531
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1532
 
        null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
 
1671
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
1672
        null_sha = b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
1533
1673
        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'),
 
1674
        root_entry = (b'', b'', b'a-root-value'), [
 
1675
            (b'd', b'', 0, False, packed_stat),
 
1676
            (b'd', b'', 0, False, b'parent-revid'),
 
1677
            ]
 
1678
        a_entry = (b'', b'a', b'a-dir'), [
 
1679
            (b'd', b'', 0, False, packed_stat),
 
1680
            (b'd', b'', 0, False, b'parent-revid'),
 
1681
            ]
 
1682
        b_entry = (b'', b'b', b'b-dir'), [
 
1683
            (b'd', b'', 0, False, packed_stat),
 
1684
            (b'd', b'', 0, False, b'parent-revid'),
 
1685
            ]
 
1686
        c_entry = (b'', b'c', b'c-file'), [
 
1687
            (b'f', null_sha, 10, False, packed_stat),
 
1688
            (b'r', b'b/j', 0, False, b''),
 
1689
            ]
 
1690
        d_entry = (b'', b'd', b'd-file'), [
 
1691
            (b'f', null_sha, 20, False, packed_stat),
 
1692
            (b'f', b'd', 20, False, b'parent-revid'),
 
1693
            ]
 
1694
        e_entry = (b'a', b'e', b'e-dir'), [
 
1695
            (b'd', b'', 0, False, packed_stat),
 
1696
            (b'd', b'', 0, False, b'parent-revid'),
 
1697
            ]
 
1698
        f_entry = (b'a', b'f', b'f-file'), [
 
1699
            (b'f', null_sha, 30, False, packed_stat),
 
1700
            (b'f', b'f', 20, False, b'parent-revid'),
 
1701
            ]
 
1702
        g_entry = (b'b', b'g', b'g-file'), [
 
1703
            (b'f', null_sha, 30, False, packed_stat),
 
1704
            NULL_PARENT_DETAILS,
 
1705
            ]
 
1706
        h_entry1 = (b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file1'), [
 
1707
            (b'f', null_sha, 40, False, packed_stat),
 
1708
            NULL_PARENT_DETAILS,
 
1709
            ]
 
1710
        h_entry2 = (b'b', b'h\xc3\xa5', b'h-\xc3\xa5-file2'), [
 
1711
            NULL_PARENT_DETAILS,
 
1712
            (b'f', b'h', 20, False, b'parent-revid'),
 
1713
            ]
 
1714
        i_entry = (b'b', b'i', b'i-file'), [
 
1715
            NULL_PARENT_DETAILS,
 
1716
            (b'f', b'h', 20, False, b'parent-revid'),
 
1717
            ]
 
1718
        j_entry = (b'b', b'j', b'c-file'), [
 
1719
            (b'r', b'c', 0, False, b''),
 
1720
            (b'f', b'j', 20, False, b'parent-revid'),
1581
1721
            ]
1582
1722
        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]))
 
1723
        dirblocks.append((b'', [root_entry]))
 
1724
        dirblocks.append((b'', [a_entry, b_entry, c_entry, d_entry]))
 
1725
        dirblocks.append((b'a', [e_entry, f_entry]))
 
1726
        dirblocks.append(
 
1727
            (b'b', [g_entry, h_entry1, h_entry2, i_entry, j_entry]))
1587
1728
        state = dirstate.DirState.initialize('dirstate')
1588
1729
        state._validate()
1589
1730
        try:
1590
 
            state._set_data(['parent'], dirblocks)
 
1731
            state._set_data([b'parent'], dirblocks)
1591
1732
        except:
1592
1733
            state.unlock()
1593
1734
            raise
1597
1738
        state, dirblocks = self.create_dirstate_with_two_trees()
1598
1739
        self.addCleanup(state.unlock)
1599
1740
        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
 
1741
        expected_result.append(dirblocks[3][1][2])  # h2
 
1742
        expected_result.append(dirblocks[3][1][3])  # i
 
1743
        expected_result.append(dirblocks[3][1][4])  # j
1603
1744
        self.assertEqual(expected_result,
1604
 
            list(state._iter_child_entries(1, 'b')))
 
1745
                         list(state._iter_child_entries(1, b'b')))
1605
1746
 
1606
1747
    def test_iter_child_root(self):
1607
1748
        state, dirblocks = self.create_dirstate_with_two_trees()
1608
1749
        self.addCleanup(state.unlock)
1609
1750
        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
 
1751
        expected_result.append(dirblocks[1][1][0])  # a
 
1752
        expected_result.append(dirblocks[1][1][1])  # b
 
1753
        expected_result.append(dirblocks[1][1][3])  # d
 
1754
        expected_result.append(dirblocks[2][1][0])  # e
 
1755
        expected_result.append(dirblocks[2][1][1])  # f
 
1756
        expected_result.append(dirblocks[3][1][2])  # h2
 
1757
        expected_result.append(dirblocks[3][1][3])  # i
 
1758
        expected_result.append(dirblocks[3][1][4])  # j
1618
1759
        self.assertEqual(expected_result,
1619
 
            list(state._iter_child_entries(1, '')))
 
1760
                         list(state._iter_child_entries(1, b'')))
1620
1761
 
1621
1762
 
1622
1763
class TestDirstateSortOrder(tests.TestCaseWithTransport):
1631
1772
        """
1632
1773
        dirs = ['a', 'a/a', 'a/a/a', 'a/a/a/a',
1633
1774
                'a-a', 'a/a-a', 'a/a/a-a', 'a/a/a/a-a',
1634
 
               ]
1635
 
        null_sha = ''
 
1775
                ]
 
1776
        null_sha = b''
1636
1777
        state = dirstate.DirState.initialize('dirstate')
1637
1778
        self.addCleanup(state.unlock)
1638
1779
 
1639
1780
        fake_stat = os.stat('dirstate')
1640
1781
        for d in dirs:
1641
 
            d_id = d.replace('/', '_')+'-id'
 
1782
            d_id = d.encode('utf-8').replace(b'/', b'_') + b'-id'
1642
1783
            file_path = d + '/f'
1643
 
            file_id = file_path.replace('/', '_')+'-id'
 
1784
            file_id = file_path.encode('utf-8').replace(b'/', b'_') + b'-id'
1644
1785
            state.add(d, d_id, 'directory', fake_stat, null_sha)
1645
1786
            state.add(file_path, file_id, 'file', fake_stat, null_sha)
1646
1787
 
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('/')
 
1788
        expected = [b'', b'', b'a',
 
1789
                    b'a/a', b'a/a/a', b'a/a/a/a',
 
1790
                    b'a/a/a/a-a', b'a/a/a-a', b'a/a-a', b'a-a',
 
1791
                    ]
 
1792
 
 
1793
        def split(p): return p.split(b'/')
1652
1794
        self.assertEqual(sorted(expected, key=split), expected)
1653
1795
        dirblock_names = [d[0] for d in state._dirblocks]
1654
1796
        self.assertEqual(expected, dirblock_names)
1656
1798
    def test_set_parent_trees_correct_order(self):
1657
1799
        """After calling set_parent_trees() we should maintain the order."""
1658
1800
        dirs = ['a', 'a-a', 'a/a']
1659
 
        null_sha = ''
 
1801
        null_sha = b''
1660
1802
        state = dirstate.DirState.initialize('dirstate')
1661
1803
        self.addCleanup(state.unlock)
1662
1804
 
1663
1805
        fake_stat = os.stat('dirstate')
1664
1806
        for d in dirs:
1665
 
            d_id = d.replace('/', '_')+'-id'
 
1807
            d_id = d.encode('utf-8').replace(b'/', b'_') + b'-id'
1666
1808
            file_path = d + '/f'
1667
 
            file_id = file_path.replace('/', '_')+'-id'
 
1809
            file_id = file_path.encode('utf-8').replace(b'/', b'_') + b'-id'
1668
1810
            state.add(d, d_id, 'directory', fake_stat, null_sha)
1669
1811
            state.add(file_path, file_id, 'file', fake_stat, null_sha)
1670
1812
 
1671
 
        expected = ['', '', 'a', 'a/a', 'a-a']
 
1813
        expected = [b'', b'', b'a', b'a/a', b'a-a']
1672
1814
        dirblock_names = [d[0] for d in state._dirblocks]
1673
1815
        self.assertEqual(expected, dirblock_names)
1674
1816
 
1684
1826
class InstrumentedDirState(dirstate.DirState):
1685
1827
    """An DirState with instrumented sha1 functionality."""
1686
1828
 
1687
 
    def __init__(self, path, sha1_provider):
1688
 
        super(InstrumentedDirState, self).__init__(path, sha1_provider)
 
1829
    def __init__(self, path, sha1_provider, worth_saving_limit=0):
 
1830
        super(InstrumentedDirState, self).__init__(path, sha1_provider,
 
1831
                                                   worth_saving_limit=worth_saving_limit)
1689
1832
        self._time_offset = 0
1690
1833
        self._log = []
1691
1834
        # member is dynamically set in DirState.__init__ to turn on trace
1738
1881
    @staticmethod
1739
1882
    def from_stat(st):
1740
1883
        return _FakeStat(st.st_size, st.st_mtime, st.st_ctime, st.st_dev,
1741
 
            st.st_ino, st.st_mode)
 
1884
                         st.st_ino, st.st_mode)
1742
1885
 
1743
1886
 
1744
1887
class TestPackStat(tests.TestCaseWithTransport):
1748
1891
        self.assertEqual(expected, dirstate.pack_stat(stat_value))
1749
1892
 
1750
1893
    def test_pack_stat_int(self):
1751
 
        st = _FakeStat(6859L, 1172758614, 1172758617, 777L, 6499538L, 0100644)
 
1894
        st = _FakeStat(6859, 1172758614, 1172758617, 777, 6499538, 0o100644)
1752
1895
        # Make sure that all parameters have an impact on the packed stat.
1753
 
        self.assertPackStat('AAAay0Xm4FZF5uBZAAADCQBjLNIAAIGk', st)
1754
 
        st.st_size = 7000L
 
1896
        self.assertPackStat(b'AAAay0Xm4FZF5uBZAAADCQBjLNIAAIGk', st)
 
1897
        st.st_size = 7000
1755
1898
        #                ay0 => bWE
1756
 
        self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
 
1899
        self.assertPackStat(b'AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1757
1900
        st.st_mtime = 1172758620
1758
1901
        #                     4FZ => 4Fx
1759
 
        self.assertPackStat('AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
 
1902
        self.assertPackStat(b'AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
1760
1903
        st.st_ctime = 1172758630
1761
1904
        #                          uBZ => uBm
1762
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1763
 
        st.st_dev = 888L
 
1905
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1906
        st.st_dev = 888
1764
1907
        #                                DCQ => DeA
1765
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNIAAIGk', st)
1766
 
        st.st_ino = 6499540L
 
1908
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADeABjLNIAAIGk', st)
 
1909
        st.st_ino = 6499540
1767
1910
        #                                     LNI => LNQ
1768
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIGk', st)
1769
 
        st.st_mode = 0100744
 
1911
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADeABjLNQAAIGk', st)
 
1912
        st.st_mode = 0o100744
1770
1913
        #                                          IGk => IHk
1771
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIHk', st)
 
1914
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADeABjLNQAAIHk', st)
1772
1915
 
1773
1916
    def test_pack_stat_float(self):
1774
1917
        """On some platforms mtime and ctime are floats.
1776
1919
        Make sure we don't get warnings or errors, and that we ignore changes <
1777
1920
        1s
1778
1921
        """
1779
 
        st = _FakeStat(7000L, 1172758614.0, 1172758617.0,
1780
 
                       777L, 6499538L, 0100644)
 
1922
        st = _FakeStat(7000, 1172758614.0, 1172758617.0,
 
1923
                       777, 6499538, 0o100644)
1781
1924
        # These should all be the same as the integer counterparts
1782
 
        self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
 
1925
        self.assertPackStat(b'AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1783
1926
        st.st_mtime = 1172758620.0
1784
1927
        #                     FZF5 => FxF5
1785
 
        self.assertPackStat('AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
 
1928
        self.assertPackStat(b'AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
1786
1929
        st.st_ctime = 1172758630.0
1787
1930
        #                          uBZ => uBm
1788
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1931
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1789
1932
        # fractional seconds are discarded, so no change from above
1790
1933
        st.st_mtime = 1172758620.453
1791
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1934
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1792
1935
        st.st_ctime = 1172758630.228
1793
 
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
 
1936
        self.assertPackStat(b'AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1794
1937
 
1795
1938
 
1796
1939
class TestBisect(TestCaseWithDirState):
1874
2017
        tree, state, expected = self.create_basic_dirstate()
1875
2018
 
1876
2019
        # 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'])
 
2020
        self.assertBisect(expected, [[b'']], state, [b''])
 
2021
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2022
        self.assertBisect(expected, [[b'b']], state, [b'b'])
 
2023
        self.assertBisect(expected, [[b'b/c']], state, [b'b/c'])
 
2024
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2025
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2026
        self.assertBisect(expected, [[b'b-c']], state, [b'b-c'])
 
2027
        self.assertBisect(expected, [[b'f']], state, [b'f'])
1885
2028
 
1886
2029
    def test_bisect_multi(self):
1887
2030
        """Bisect can be used to find multiple records at the same time."""
1888
2031
        tree, state, expected = self.create_basic_dirstate()
1889
2032
        # 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'])
 
2033
        self.assertBisect(expected, [[b'a'], [b'b'], [b'f']],
 
2034
                          state, [b'a', b'b', b'f'])
 
2035
        self.assertBisect(expected, [[b'f'], [b'b/d'], [b'b/d/e']],
 
2036
                          state, [b'f', b'b/d', b'b/d/e'])
 
2037
        self.assertBisect(expected, [[b'b'], [b'b-c'], [b'b/c']],
 
2038
                          state, [b'b', b'b-c', b'b/c'])
1896
2039
 
1897
2040
    def test_bisect_one_page(self):
1898
2041
        """Test bisect when there is only 1 page to read"""
1899
2042
        tree, state, expected = self.create_basic_dirstate()
1900
2043
        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'])
 
2044
        self.assertBisect(expected, [[b'']], state, [b''])
 
2045
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2046
        self.assertBisect(expected, [[b'b']], state, [b'b'])
 
2047
        self.assertBisect(expected, [[b'b/c']], state, [b'b/c'])
 
2048
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2049
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2050
        self.assertBisect(expected, [[b'b-c']], state, [b'b-c'])
 
2051
        self.assertBisect(expected, [[b'f']], state, [b'f'])
 
2052
        self.assertBisect(expected, [[b'a'], [b'b'], [b'f']],
 
2053
                          state, [b'a', b'b', b'f'])
 
2054
        self.assertBisect(expected, [[b'b/d'], [b'b/d/e'], [b'f']],
 
2055
                          state, [b'b/d', b'b/d/e', b'f'])
 
2056
        self.assertBisect(expected, [[b'b'], [b'b/c'], [b'b-c']],
 
2057
                          state, [b'b', b'b/c', b'b-c'])
1915
2058
 
1916
2059
    def test_bisect_duplicate_paths(self):
1917
2060
        """When bisecting for a path, handle multiple entries."""
1918
2061
        tree, state, expected = self.create_duplicated_dirstate()
1919
2062
 
1920
2063
        # 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'])
 
2064
        self.assertBisect(expected, [[b'']], state, [b''])
 
2065
        self.assertBisect(expected, [[b'a', b'a2']], state, [b'a'])
 
2066
        self.assertBisect(expected, [[b'b', b'b2']], state, [b'b'])
 
2067
        self.assertBisect(expected, [[b'b/c', b'b/c2']], state, [b'b/c'])
 
2068
        self.assertBisect(expected, [[b'b/d', b'b/d2']], state, [b'b/d'])
 
2069
        self.assertBisect(expected, [[b'b/d/e', b'b/d/e2']],
 
2070
                          state, [b'b/d/e'])
 
2071
        self.assertBisect(expected, [[b'b-c', b'b-c2']], state, [b'b-c'])
 
2072
        self.assertBisect(expected, [[b'f', b'f2']], state, [b'f'])
1930
2073
 
1931
2074
    def test_bisect_page_size_too_small(self):
1932
2075
        """If the page size is too small, we will auto increase it."""
1933
2076
        tree, state, expected = self.create_basic_dirstate()
1934
2077
        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'])
 
2078
        self.assertBisect(expected, [None], state, [b'b/e'])
 
2079
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2080
        self.assertBisect(expected, [[b'b']], state, [b'b'])
 
2081
        self.assertBisect(expected, [[b'b/c']], state, [b'b/c'])
 
2082
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2083
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2084
        self.assertBisect(expected, [[b'b-c']], state, [b'b-c'])
 
2085
        self.assertBisect(expected, [[b'f']], state, [b'f'])
1943
2086
 
1944
2087
    def test_bisect_missing(self):
1945
2088
        """Test that bisect return None if it cannot find a path."""
1946
2089
        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'])
 
2090
        self.assertBisect(expected, [None], state, [b'foo'])
 
2091
        self.assertBisect(expected, [None], state, [b'b/foo'])
 
2092
        self.assertBisect(expected, [None], state, [b'bar/foo'])
 
2093
        self.assertBisect(expected, [None], state, [b'b-c/foo'])
1951
2094
 
1952
 
        self.assertBisect(expected, [['a'], None, ['b/d']],
1953
 
                          state, ['a', 'foo', 'b/d'])
 
2095
        self.assertBisect(expected, [[b'a'], None, [b'b/d']],
 
2096
                          state, [b'a', b'foo', b'b/d'])
1954
2097
 
1955
2098
    def test_bisect_rename(self):
1956
2099
        """Check that we find a renamed row."""
1957
2100
        tree, state, expected = self.create_renamed_dirstate()
1958
2101
 
1959
2102
        # 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'])
 
2103
        self.assertBisect(expected, [[b'a']], state, [b'a'])
 
2104
        self.assertBisect(expected, [[b'b/g']], state, [b'b/g'])
 
2105
        self.assertBisect(expected, [[b'b/d']], state, [b'b/d'])
 
2106
        self.assertBisect(expected, [[b'h']], state, [b'h'])
1964
2107
 
1965
2108
        # 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'])
 
2109
        self.assertBisect(expected, [[b'b/d/e']], state, [b'b/d/e'])
 
2110
        self.assertBisect(expected, [[b'h/e']], state, [b'h/e'])
1968
2111
 
1969
2112
    def test_bisect_dirblocks(self):
1970
2113
        tree, state, expected = self.create_duplicated_dirstate()
1971
2114
        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'])
 
2115
                                   [[b'', b'a', b'a2', b'b', b'b2',
 
2116
                                       b'b-c', b'b-c2', b'f', b'f2']],
 
2117
                                   state, [b''])
 
2118
        self.assertBisectDirBlocks(expected,
 
2119
                                   [[b'b/c', b'b/c2', b'b/d', b'b/d2']], state, [b'b'])
 
2120
        self.assertBisectDirBlocks(expected,
 
2121
                                   [[b'b/d/e', b'b/d/e2']], state, [b'b/d'])
 
2122
        self.assertBisectDirBlocks(expected,
 
2123
                                   [[b'', b'a', b'a2', b'b', b'b2', b'b-c', b'b-c2', b'f', b'f2'],
 
2124
                                    [b'b/c', b'b/c2', b'b/d', b'b/d2'],
 
2125
                                       [b'b/d/e', b'b/d/e2'],
 
2126
                                    ], state, [b'', b'b', b'b/d'])
1983
2127
 
1984
2128
    def test_bisect_dirblocks_missing(self):
1985
2129
        tree, state, expected = self.create_basic_dirstate()
1986
 
        self.assertBisectDirBlocks(expected, [['b/d/e'], None],
1987
 
            state, ['b/d', 'b/e'])
 
2130
        self.assertBisectDirBlocks(expected, [[b'b/d/e'], None],
 
2131
                                   state, [b'b/d', b'b/e'])
1988
2132
        # 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'])
 
2133
        self.assertBisectDirBlocks(expected, [None], state, [b'a'])
 
2134
        self.assertBisectDirBlocks(expected, [None], state, [b'b/c'])
 
2135
        self.assertBisectDirBlocks(expected, [None], state, [b'c'])
 
2136
        self.assertBisectDirBlocks(expected, [None], state, [b'b/d/e'])
 
2137
        self.assertBisectDirBlocks(expected, [None], state, [b'f'])
1994
2138
 
1995
2139
    def test_bisect_recursive_each(self):
1996
2140
        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, [''])
 
2141
        self.assertBisectRecursive(expected, [b'a'], state, [b'a'])
 
2142
        self.assertBisectRecursive(expected, [b'b/c'], state, [b'b/c'])
 
2143
        self.assertBisectRecursive(expected, [b'b/d/e'], state, [b'b/d/e'])
 
2144
        self.assertBisectRecursive(expected, [b'b-c'], state, [b'b-c'])
 
2145
        self.assertBisectRecursive(expected, [b'b/d', b'b/d/e'],
 
2146
                                   state, [b'b/d'])
 
2147
        self.assertBisectRecursive(expected, [b'b', b'b/c', b'b/d', b'b/d/e'],
 
2148
                                   state, [b'b'])
 
2149
        self.assertBisectRecursive(expected, [b'', b'a', b'b', b'b-c', b'f', b'b/c',
 
2150
                                              b'b/d', b'b/d/e'],
 
2151
                                   state, [b''])
2008
2152
 
2009
2153
    def test_bisect_recursive_multiple(self):
2010
2154
        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'])
 
2155
        self.assertBisectRecursive(
 
2156
            expected, [b'a', b'b/c'], state, [b'a', b'b/c'])
 
2157
        self.assertBisectRecursive(expected, [b'b/d', b'b/d/e'],
 
2158
                                   state, [b'b/d', b'b/d/e'])
2014
2159
 
2015
2160
    def test_bisect_recursive_missing(self):
2016
2161
        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'])
 
2162
        self.assertBisectRecursive(expected, [], state, [b'd'])
 
2163
        self.assertBisectRecursive(expected, [], state, [b'b/e'])
 
2164
        self.assertBisectRecursive(expected, [], state, [b'g'])
 
2165
        self.assertBisectRecursive(expected, [b'a'], state, [b'a', b'g'])
2021
2166
 
2022
2167
    def test_bisect_recursive_renamed(self):
2023
2168
        tree, state, expected = self.create_renamed_dirstate()
2024
2169
 
2025
2170
        # 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'])
 
2171
        self.assertBisectRecursive(expected, [b'a', b'b/g'], state, [b'a'])
 
2172
        self.assertBisectRecursive(expected, [b'a', b'b/g'], state, [b'b/g'])
2028
2173
        # Looking in the containing directory should find the rename target,
2029
2174
        # 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'])
 
2175
        self.assertBisectRecursive(expected, [b'a', b'b', b'b/c', b'b/d',
 
2176
                                              b'b/d/e', b'b/g', b'h', b'h/e'],
 
2177
                                   state, [b'b'])
2033
2178
 
2034
2179
 
2035
2180
class TestDirstateValidation(TestCaseWithDirState):
2052
2197
        # we're appending to the dirblock, but this name comes before some of
2053
2198
        # the existing names; that's wrong
2054
2199
        last_dirblock[1].append(
2055
 
            (('h', 'aaaa', 'a-id'),
2056
 
             [('a', '', 0, False, ''),
2057
 
              ('a', '', 0, False, '')]))
 
2200
            ((b'h', b'aaaa', b'a-id'),
 
2201
             [(b'a', b'', 0, False, b''),
 
2202
              (b'a', b'', 0, False, b'')]))
2058
2203
        e = self.assertRaises(AssertionError,
2059
 
            state._validate)
 
2204
                              state._validate)
2060
2205
        self.assertContainsRe(str(e), 'not sorted')
2061
2206
 
2062
2207
    def test_dirblock_name_mismatch(self):
2065
2210
        last_dirblock = state._dirblocks[-1]
2066
2211
        # add an entry with the wrong directory name
2067
2212
        last_dirblock[1].append(
2068
 
            (('', 'z', 'a-id'),
2069
 
             [('a', '', 0, False, ''),
2070
 
              ('a', '', 0, False, '')]))
 
2213
            ((b'', b'z', b'a-id'),
 
2214
             [(b'a', b'', 0, False, b''),
 
2215
              (b'a', b'', 0, False, b'')]))
2071
2216
        e = self.assertRaises(AssertionError,
2072
 
            state._validate)
 
2217
                              state._validate)
2073
2218
        self.assertContainsRe(str(e),
2074
 
            "doesn't match directory name")
 
2219
                              "doesn't match directory name")
2075
2220
 
2076
2221
    def test_dirblock_missing_rename(self):
2077
2222
        tree, state, expected = self.create_renamed_dirstate()
2080
2225
        # make another entry for a-id, without a correct 'r' pointer to
2081
2226
        # the real occurrence in the working tree
2082
2227
        last_dirblock[1].append(
2083
 
            (('h', 'z', 'a-id'),
2084
 
             [('a', '', 0, False, ''),
2085
 
              ('a', '', 0, False, '')]))
 
2228
            ((b'h', b'z', b'a-id'),
 
2229
             [(b'a', b'', 0, False, b''),
 
2230
              (b'a', b'', 0, False, b'')]))
2086
2231
        e = self.assertRaises(AssertionError,
2087
 
            state._validate)
 
2232
                              state._validate)
2088
2233
        self.assertContainsRe(str(e),
2089
 
            'file a-id is absent in row')
 
2234
                              'file a-id is absent in row')
2090
2235
 
2091
2236
 
2092
2237
class TestDirstateTreeReference(TestCaseWithDirState):
2093
2238
 
2094
2239
    def test_reference_revision_is_none(self):
2095
 
        tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
 
2240
        tree = self.make_branch_and_tree('tree', format='development-subtree')
2096
2241
        subtree = self.make_branch_and_tree('tree/subtree',
2097
 
                            format='dirstate-with-subtree')
2098
 
        subtree.set_root_id('subtree')
 
2242
                                            format='development-subtree')
 
2243
        subtree.set_root_id(b'subtree')
2099
2244
        tree.add_reference(subtree)
2100
2245
        tree.add('subtree')
2101
2246
        state = dirstate.DirState.from_tree(tree, 'dirstate')
2102
 
        key = ('', 'subtree', 'subtree')
2103
 
        expected = ('', [(key,
2104
 
            [('t', '', 0, False, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])])
 
2247
        key = (b'', b'subtree', b'subtree')
 
2248
        expected = (b'', [(key,
 
2249
                           [(b't', b'', 0, False, b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])])
2105
2250
 
2106
2251
        try:
2107
2252
            self.assertEqual(expected, state._find_block(key))
2120
2265
 
2121
2266
    def test_discard_one_parent(self):
2122
2267
        # 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),
 
2268
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
2269
        root_entry_direntry = (b'', b'', b'a-root-value'), [
 
2270
            (b'd', b'', 0, False, packed_stat),
 
2271
            (b'd', b'', 0, False, packed_stat),
2127
2272
            ]
2128
2273
        dirblocks = []
2129
 
        dirblocks.append(('', [root_entry_direntry]))
2130
 
        dirblocks.append(('', []))
 
2274
        dirblocks.append((b'', [root_entry_direntry]))
 
2275
        dirblocks.append((b'', []))
2131
2276
 
2132
2277
        state = self.create_empty_dirstate()
2133
2278
        self.addCleanup(state.unlock)
2134
 
        state._set_data(['parent-id'], dirblocks[:])
 
2279
        state._set_data([b'parent-id'], dirblocks[:])
2135
2280
        state._validate()
2136
2281
 
2137
2282
        state._discard_merge_parents()
2140
2285
 
2141
2286
    def test_discard_simple(self):
2142
2287
        # 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),
 
2288
        packed_stat = b'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
2289
        root_entry_direntry = (b'', b'', b'a-root-value'), [
 
2290
            (b'd', b'', 0, False, packed_stat),
 
2291
            (b'd', b'', 0, False, packed_stat),
 
2292
            (b'd', b'', 0, False, packed_stat),
2148
2293
            ]
2149
 
        expected_root_entry_direntry = ('', '', 'a-root-value'), [
2150
 
            ('d', '', 0, False, packed_stat),
2151
 
            ('d', '', 0, False, packed_stat),
 
2294
        expected_root_entry_direntry = (b'', b'', b'a-root-value'), [
 
2295
            (b'd', b'', 0, False, packed_stat),
 
2296
            (b'd', b'', 0, False, packed_stat),
2152
2297
            ]
2153
2298
        dirblocks = []
2154
 
        dirblocks.append(('', [root_entry_direntry]))
2155
 
        dirblocks.append(('', []))
 
2299
        dirblocks.append((b'', [root_entry_direntry]))
 
2300
        dirblocks.append((b'', []))
2156
2301
 
2157
2302
        state = self.create_empty_dirstate()
2158
2303
        self.addCleanup(state.unlock)
2159
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2304
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2160
2305
        state._validate()
2161
2306
 
2162
2307
        # This should strip of the extra column
2163
2308
        state._discard_merge_parents()
2164
2309
        state._validate()
2165
 
        expected_dirblocks = [('', [expected_root_entry_direntry]), ('', [])]
 
2310
        expected_dirblocks = [(b'', [expected_root_entry_direntry]), (b'', [])]
2166
2311
        self.assertEqual(expected_dirblocks, state._dirblocks)
2167
2312
 
2168
2313
    def test_discard_absent(self):
2169
2314
        """If entries are only in a merge, discard should remove the entries"""
2170
2315
        null_stat = dirstate.DirState.NULLSTAT
2171
 
        present_dir = ('d', '', 0, False, null_stat)
2172
 
        present_file = ('f', '', 0, False, null_stat)
 
2316
        present_dir = (b'd', b'', 0, False, null_stat)
 
2317
        present_file = (b'f', b'', 0, False, null_stat)
2173
2318
        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
 
                    ]
 
2319
        root_key = (b'', b'', b'a-root-value')
 
2320
        file_in_root_key = (b'', b'file-in-root', b'a-file-id')
 
2321
        file_in_merged_key = (b'', b'file-in-merged', b'b-file-id')
 
2322
        dirblocks = [(b'', [(root_key, [present_dir, present_dir, present_dir])]),
 
2323
                     (b'', [(file_in_merged_key,
 
2324
                             [absent, absent, present_file]),
 
2325
                            (file_in_root_key,
 
2326
                             [present_file, present_file, present_file]),
 
2327
                            ]),
 
2328
                     ]
2184
2329
 
2185
2330
        state = self.create_empty_dirstate()
2186
2331
        self.addCleanup(state.unlock)
2187
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2332
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2188
2333
        state._validate()
2189
2334
 
2190
 
        exp_dirblocks = [('', [(root_key, [present_dir, present_dir])]),
2191
 
                         ('', [(file_in_root_key,
2192
 
                                [present_file, present_file]),
2193
 
                              ]),
2194
 
                        ]
 
2335
        exp_dirblocks = [(b'', [(root_key, [present_dir, present_dir])]),
 
2336
                         (b'', [(file_in_root_key,
 
2337
                                 [present_file, present_file]),
 
2338
                                ]),
 
2339
                         ]
2195
2340
        state._discard_merge_parents()
2196
2341
        state._validate()
2197
2342
        self.assertEqual(exp_dirblocks, state._dirblocks)
2198
2343
 
2199
2344
    def test_discard_renamed(self):
2200
2345
        null_stat = dirstate.DirState.NULLSTAT
2201
 
        present_dir = ('d', '', 0, False, null_stat)
2202
 
        present_file = ('f', '', 0, False, null_stat)
 
2346
        present_dir = (b'd', b'', 0, False, null_stat)
 
2347
        present_file = (b'f', b'', 0, False, null_stat)
2203
2348
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2204
 
        root_key = ('', '', 'a-root-value')
2205
 
        file_in_root_key = ('', 'file-in-root', 'a-file-id')
 
2349
        root_key = (b'', b'', b'a-root-value')
 
2350
        file_in_root_key = (b'', b'file-in-root', b'a-file-id')
2206
2351
        # Renamed relative to parent
2207
 
        file_rename_s_key = ('', 'file-s', 'b-file-id')
2208
 
        file_rename_t_key = ('', 'file-t', 'b-file-id')
 
2352
        file_rename_s_key = (b'', b'file-s', b'b-file-id')
 
2353
        file_rename_t_key = (b'', b'file-t', b'b-file-id')
2209
2354
        # 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')
 
2355
        key_in_1 = (b'', b'file-in-1', b'c-file-id')
 
2356
        key_in_2 = (b'', b'file-in-2', b'c-file-id')
2212
2357
 
2213
2358
        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
 
                 ]),
 
2359
            (b'', [(root_key, [present_dir, present_dir, present_dir])]),
 
2360
            (b'', [(key_in_1,
 
2361
                    [absent, present_file, (b'r', b'file-in-2', b'c-file-id')]),
 
2362
                   (key_in_2,
 
2363
                    [absent, (b'r', b'file-in-1', b'c-file-id'), present_file]),
 
2364
                   (file_in_root_key,
 
2365
                    [present_file, present_file, present_file]),
 
2366
                   (file_rename_s_key,
 
2367
                    [(b'r', b'file-t', b'b-file-id'), absent, present_file]),
 
2368
                   (file_rename_t_key,
 
2369
                    [present_file, absent, (b'r', b'file-s', b'b-file-id')]),
 
2370
                   ]),
2226
2371
        ]
2227
2372
        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
 
                 ]),
 
2373
            (b'', [(root_key, [present_dir, present_dir])]),
 
2374
            (b'', [(key_in_1, [absent, present_file]),
 
2375
                   (file_in_root_key, [present_file, present_file]),
 
2376
                   (file_rename_t_key, [present_file, absent]),
 
2377
                   ]),
2233
2378
        ]
2234
2379
        state = self.create_empty_dirstate()
2235
2380
        self.addCleanup(state.unlock)
2236
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2381
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2237
2382
        state._validate()
2238
2383
 
2239
2384
        state._discard_merge_parents()
2242
2387
 
2243
2388
    def test_discard_all_subdir(self):
2244
2389
        null_stat = dirstate.DirState.NULLSTAT
2245
 
        present_dir = ('d', '', 0, False, null_stat)
2246
 
        present_file = ('f', '', 0, False, null_stat)
 
2390
        present_dir = (b'd', b'', 0, False, null_stat)
 
2391
        present_file = (b'f', b'', 0, False, null_stat)
2247
2392
        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')
 
2393
        root_key = (b'', b'', b'a-root-value')
 
2394
        subdir_key = (b'', b'sub', b'dir-id')
 
2395
        child1_key = (b'sub', b'child1', b'child1-id')
 
2396
        child2_key = (b'sub', b'child2', b'child2-id')
 
2397
        child3_key = (b'sub', b'child3', b'child3-id')
2253
2398
 
2254
2399
        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
 
                    ]),
 
2400
            (b'', [(root_key, [present_dir, present_dir, present_dir])]),
 
2401
            (b'', [(subdir_key, [present_dir, present_dir, present_dir])]),
 
2402
            (b'sub', [(child1_key, [absent, absent, present_file]),
 
2403
                      (child2_key, [absent, absent, present_file]),
 
2404
                      (child3_key, [absent, absent, present_file]),
 
2405
                      ]),
2261
2406
        ]
2262
2407
        exp_dirblocks = [
2263
 
            ('', [(root_key, [present_dir, present_dir])]),
2264
 
            ('', [(subdir_key, [present_dir, present_dir])]),
2265
 
            ('sub', []),
 
2408
            (b'', [(root_key, [present_dir, present_dir])]),
 
2409
            (b'', [(subdir_key, [present_dir, present_dir])]),
 
2410
            (b'sub', []),
2266
2411
        ]
2267
2412
        state = self.create_empty_dirstate()
2268
2413
        self.addCleanup(state.unlock)
2269
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
 
2414
        state._set_data([b'parent-id', b'merged-id'], dirblocks[:])
2270
2415
        state._validate()
2271
2416
 
2272
2417
        state._discard_merge_parents()
2282
2427
        # details should always allow join() and always be a plain str when
2283
2428
        # finished
2284
2429
        (minikind, fingerprint, size, executable, tree_data) = details
2285
 
        self.assertIsInstance(minikind, str)
2286
 
        self.assertIsInstance(fingerprint, str)
2287
 
        self.assertIsInstance(tree_data, str)
 
2430
        self.assertIsInstance(minikind, bytes)
 
2431
        self.assertIsInstance(fingerprint, bytes)
 
2432
        self.assertIsInstance(tree_data, bytes)
2288
2433
 
2289
2434
    def test_unicode_symlink(self):
2290
 
        inv_entry = inventory.InventoryLink('link-file-id',
 
2435
        inv_entry = inventory.InventoryLink(b'link-file-id',
2291
2436
                                            u'nam\N{Euro Sign}e',
2292
 
                                            'link-parent-id')
2293
 
        inv_entry.revision = 'link-revision-id'
 
2437
                                            b'link-parent-id')
 
2438
        inv_entry.revision = b'link-revision-id'
2294
2439
        target = u'link-targ\N{Euro Sign}t'
2295
2440
        inv_entry.symlink_target = target
2296
 
        self.assertDetails(('l', target.encode('UTF-8'), 0, False,
2297
 
                            'link-revision-id'), inv_entry)
 
2441
        self.assertDetails((b'l', target.encode('UTF-8'), 0, False,
 
2442
                            b'link-revision-id'), inv_entry)
2298
2443
 
2299
2444
 
2300
2445
class TestSHA1Provider(tests.TestCaseInTempDir):
2305
2450
        self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2306
2451
 
2307
2452
    def test_defaultsha1provider_sha1(self):
2308
 
        text = 'test\r\nwith\nall\rpossible line endings\r\n'
 
2453
        text = b'test\r\nwith\nall\rpossible line endings\r\n'
2309
2454
        self.build_tree_contents([('foo', text)])
2310
2455
        expected_sha = osutils.sha_string(text)
2311
2456
        p = dirstate.DefaultSHA1Provider()
2312
2457
        self.assertEqual(expected_sha, p.sha1('foo'))
2313
2458
 
2314
2459
    def test_defaultsha1provider_stat_and_sha1(self):
2315
 
        text = 'test\r\nwith\nall\rpossible line endings\r\n'
 
2460
        text = b'test\r\nwith\nall\rpossible line endings\r\n'
2316
2461
        self.build_tree_contents([('foo', text)])
2317
2462
        expected_sha = osutils.sha_string(text)
2318
2463
        p = dirstate.DefaultSHA1Provider()
2320
2465
        self.assertTrue(len(statvalue) >= 10)
2321
2466
        self.assertEqual(len(text), statvalue.st_size)
2322
2467
        self.assertEqual(expected_sha, sha1)
 
2468
 
 
2469
 
 
2470
class _Repo(object):
 
2471
    """A minimal api to get InventoryRevisionTree to work."""
 
2472
 
 
2473
    def __init__(self):
 
2474
        default_format = controldir.format_registry.make_controldir('default')
 
2475
        self._format = default_format.repository_format
 
2476
 
 
2477
    def lock_read(self):
 
2478
        pass
 
2479
 
 
2480
    def unlock(self):
 
2481
        pass
 
2482
 
 
2483
 
 
2484
class TestUpdateBasisByDelta(tests.TestCase):
 
2485
 
 
2486
    def path_to_ie(self, path, file_id, rev_id, dir_ids):
 
2487
        if path.endswith('/'):
 
2488
            is_dir = True
 
2489
            path = path[:-1]
 
2490
        else:
 
2491
            is_dir = False
 
2492
        dirname, basename = osutils.split(path)
 
2493
        try:
 
2494
            dir_id = dir_ids[dirname]
 
2495
        except KeyError:
 
2496
            dir_id = osutils.basename(dirname).encode('utf-8') + b'-id'
 
2497
        if is_dir:
 
2498
            ie = inventory.InventoryDirectory(file_id, basename, dir_id)
 
2499
            dir_ids[path] = file_id
 
2500
        else:
 
2501
            ie = inventory.InventoryFile(file_id, basename, dir_id)
 
2502
            ie.text_size = 0
 
2503
            ie.text_sha1 = b''
 
2504
        ie.revision = rev_id
 
2505
        return ie
 
2506
 
 
2507
    def create_tree_from_shape(self, rev_id, shape):
 
2508
        dir_ids = {'': b'root-id'}
 
2509
        inv = inventory.Inventory(b'root-id', rev_id)
 
2510
        for info in shape:
 
2511
            if len(info) == 2:
 
2512
                path, file_id = info
 
2513
                ie_rev_id = rev_id
 
2514
            else:
 
2515
                path, file_id, ie_rev_id = info
 
2516
            if path == '':
 
2517
                # Replace the root entry
 
2518
                del inv._byid[inv.root.file_id]
 
2519
                inv.root.file_id = file_id
 
2520
                inv._byid[file_id] = inv.root
 
2521
                dir_ids[''] = file_id
 
2522
                continue
 
2523
            inv.add(self.path_to_ie(path, file_id, ie_rev_id, dir_ids))
 
2524
        return inventorytree.InventoryRevisionTree(_Repo(), inv, rev_id)
 
2525
 
 
2526
    def create_empty_dirstate(self):
 
2527
        fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
 
2528
        self.addCleanup(os.remove, path)
 
2529
        os.close(fd)
 
2530
        state = dirstate.DirState.initialize(path)
 
2531
        self.addCleanup(state.unlock)
 
2532
        return state
 
2533
 
 
2534
    def create_inv_delta(self, delta, rev_id):
 
2535
        """Translate a 'delta shape' into an actual InventoryDelta"""
 
2536
        dir_ids = {'': b'root-id'}
 
2537
        inv_delta = []
 
2538
        for old_path, new_path, file_id in delta:
 
2539
            if old_path is not None and old_path.endswith('/'):
 
2540
                # Don't have to actually do anything for this, because only
 
2541
                # new_path creates InventoryEntries
 
2542
                old_path = old_path[:-1]
 
2543
            if new_path is None:  # Delete
 
2544
                inv_delta.append((old_path, None, file_id, None))
 
2545
                continue
 
2546
            ie = self.path_to_ie(new_path, file_id, rev_id, dir_ids)
 
2547
            inv_delta.append((old_path, new_path, file_id, ie))
 
2548
        return inv_delta
 
2549
 
 
2550
    def assertUpdate(self, active, basis, target):
 
2551
        """Assert that update_basis_by_delta works how we want.
 
2552
 
 
2553
        Set up a DirState object with active_shape for tree 0, basis_shape for
 
2554
        tree 1. Then apply the delta from basis_shape to target_shape,
 
2555
        and assert that the DirState is still valid, and that its stored
 
2556
        content matches the target_shape.
 
2557
        """
 
2558
        active_tree = self.create_tree_from_shape(b'active', active)
 
2559
        basis_tree = self.create_tree_from_shape(b'basis', basis)
 
2560
        target_tree = self.create_tree_from_shape(b'target', target)
 
2561
        state = self.create_empty_dirstate()
 
2562
        state.set_state_from_scratch(active_tree.root_inventory,
 
2563
                                     [(b'basis', basis_tree)], [])
 
2564
        delta = target_tree.root_inventory._make_delta(
 
2565
            basis_tree.root_inventory)
 
2566
        state.update_basis_by_delta(delta, b'target')
 
2567
        state._validate()
 
2568
        dirstate_tree = workingtree_4.DirStateRevisionTree(state,
 
2569
                                                           b'target', _Repo())
 
2570
        # The target now that delta has been applied should match the
 
2571
        # RevisionTree
 
2572
        self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
 
2573
        # And the dirblock state should be identical to the state if we created
 
2574
        # it from scratch.
 
2575
        state2 = self.create_empty_dirstate()
 
2576
        state2.set_state_from_scratch(active_tree.root_inventory,
 
2577
                                      [(b'target', target_tree)], [])
 
2578
        self.assertEqual(state2._dirblocks, state._dirblocks)
 
2579
        return state
 
2580
 
 
2581
    def assertBadDelta(self, active, basis, delta):
 
2582
        """Test that we raise InconsistentDelta when appropriate.
 
2583
 
 
2584
        :param active: The active tree shape
 
2585
        :param basis: The basis tree shape
 
2586
        :param delta: A description of the delta to apply. Similar to the form
 
2587
            for regular inventory deltas, but omitting the InventoryEntry.
 
2588
            So adding a file is: (None, 'path', b'file-id')
 
2589
            Adding a directory is: (None, 'path/', b'dir-id')
 
2590
            Renaming a dir is: ('old/', 'new/', b'dir-id')
 
2591
            etc.
 
2592
        """
 
2593
        active_tree = self.create_tree_from_shape(b'active', active)
 
2594
        basis_tree = self.create_tree_from_shape(b'basis', basis)
 
2595
        inv_delta = self.create_inv_delta(delta, b'target')
 
2596
        state = self.create_empty_dirstate()
 
2597
        state.set_state_from_scratch(active_tree.root_inventory,
 
2598
                                     [(b'basis', basis_tree)], [])
 
2599
        self.assertRaises(errors.InconsistentDelta,
 
2600
                          state.update_basis_by_delta, inv_delta, b'target')
 
2601
        # try:
 
2602
        ##     state.update_basis_by_delta(inv_delta, b'target')
 
2603
        # except errors.InconsistentDelta, e:
 
2604
        ##     import pdb; pdb.set_trace()
 
2605
        # else:
 
2606
        ##     import pdb; pdb.set_trace()
 
2607
        self.assertTrue(state._changes_aborted)
 
2608
 
 
2609
    def test_remove_file_matching_active_state(self):
 
2610
        state = self.assertUpdate(
 
2611
            active=[],
 
2612
            basis=[('file', b'file-id')],
 
2613
            target=[],
 
2614
            )
 
2615
 
 
2616
    def test_remove_file_present_in_active_state(self):
 
2617
        state = self.assertUpdate(
 
2618
            active=[('file', b'file-id')],
 
2619
            basis=[('file', b'file-id')],
 
2620
            target=[],
 
2621
            )
 
2622
 
 
2623
    def test_remove_file_present_elsewhere_in_active_state(self):
 
2624
        state = self.assertUpdate(
 
2625
            active=[('other-file', b'file-id')],
 
2626
            basis=[('file', b'file-id')],
 
2627
            target=[],
 
2628
            )
 
2629
 
 
2630
    def test_remove_file_active_state_has_diff_file(self):
 
2631
        state = self.assertUpdate(
 
2632
            active=[('file', b'file-id-2')],
 
2633
            basis=[('file', b'file-id')],
 
2634
            target=[],
 
2635
            )
 
2636
 
 
2637
    def test_remove_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2638
        state = self.assertUpdate(
 
2639
            active=[('file', b'file-id-2'),
 
2640
                    ('other-file', b'file-id')],
 
2641
            basis=[('file', b'file-id')],
 
2642
            target=[],
 
2643
            )
 
2644
 
 
2645
    def test_add_file_matching_active_state(self):
 
2646
        state = self.assertUpdate(
 
2647
            active=[('file', b'file-id')],
 
2648
            basis=[],
 
2649
            target=[('file', b'file-id')],
 
2650
            )
 
2651
 
 
2652
    def test_add_file_in_empty_dir_not_matching_active_state(self):
 
2653
        state = self.assertUpdate(
 
2654
            active=[],
 
2655
            basis=[('dir/', b'dir-id')],
 
2656
            target=[('dir/', b'dir-id', b'basis'), ('dir/file', b'file-id')],
 
2657
            )
 
2658
 
 
2659
    def test_add_file_missing_in_active_state(self):
 
2660
        state = self.assertUpdate(
 
2661
            active=[],
 
2662
            basis=[],
 
2663
            target=[('file', b'file-id')],
 
2664
            )
 
2665
 
 
2666
    def test_add_file_elsewhere_in_active_state(self):
 
2667
        state = self.assertUpdate(
 
2668
            active=[('other-file', b'file-id')],
 
2669
            basis=[],
 
2670
            target=[('file', b'file-id')],
 
2671
            )
 
2672
 
 
2673
    def test_add_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2674
        state = self.assertUpdate(
 
2675
            active=[('other-file', b'file-id'),
 
2676
                    ('file', b'file-id-2')],
 
2677
            basis=[],
 
2678
            target=[('file', b'file-id')],
 
2679
            )
 
2680
 
 
2681
    def test_rename_file_matching_active_state(self):
 
2682
        state = self.assertUpdate(
 
2683
            active=[('other-file', b'file-id')],
 
2684
            basis=[('file', b'file-id')],
 
2685
            target=[('other-file', b'file-id')],
 
2686
            )
 
2687
 
 
2688
    def test_rename_file_missing_in_active_state(self):
 
2689
        state = self.assertUpdate(
 
2690
            active=[],
 
2691
            basis=[('file', b'file-id')],
 
2692
            target=[('other-file', b'file-id')],
 
2693
            )
 
2694
 
 
2695
    def test_rename_file_present_elsewhere_in_active_state(self):
 
2696
        state = self.assertUpdate(
 
2697
            active=[('third', b'file-id')],
 
2698
            basis=[('file', b'file-id')],
 
2699
            target=[('other-file', b'file-id')],
 
2700
            )
 
2701
 
 
2702
    def test_rename_file_active_state_has_diff_source_file(self):
 
2703
        state = self.assertUpdate(
 
2704
            active=[('file', b'file-id-2')],
 
2705
            basis=[('file', b'file-id')],
 
2706
            target=[('other-file', b'file-id')],
 
2707
            )
 
2708
 
 
2709
    def test_rename_file_active_state_has_diff_target_file(self):
 
2710
        state = self.assertUpdate(
 
2711
            active=[('other-file', b'file-id-2')],
 
2712
            basis=[('file', b'file-id')],
 
2713
            target=[('other-file', b'file-id')],
 
2714
            )
 
2715
 
 
2716
    def test_rename_file_active_has_swapped_files(self):
 
2717
        state = self.assertUpdate(
 
2718
            active=[('file', b'file-id'),
 
2719
                    ('other-file', b'file-id-2')],
 
2720
            basis=[('file', b'file-id'),
 
2721
                   ('other-file', b'file-id-2')],
 
2722
            target=[('file', b'file-id-2'),
 
2723
                    ('other-file', b'file-id')])
 
2724
 
 
2725
    def test_rename_file_basis_has_swapped_files(self):
 
2726
        state = self.assertUpdate(
 
2727
            active=[('file', b'file-id'),
 
2728
                    ('other-file', b'file-id-2')],
 
2729
            basis=[('file', b'file-id-2'),
 
2730
                   ('other-file', b'file-id')],
 
2731
            target=[('file', b'file-id'),
 
2732
                    ('other-file', b'file-id-2')])
 
2733
 
 
2734
    def test_rename_directory_with_contents(self):
 
2735
        state = self.assertUpdate(  # active matches basis
 
2736
            active=[('dir1/', b'dir-id'),
 
2737
                    ('dir1/file', b'file-id')],
 
2738
            basis=[('dir1/', b'dir-id'),
 
2739
                   ('dir1/file', b'file-id')],
 
2740
            target=[('dir2/', b'dir-id'),
 
2741
                    ('dir2/file', b'file-id')])
 
2742
        state = self.assertUpdate(  # active matches target
 
2743
            active=[('dir2/', b'dir-id'),
 
2744
                    ('dir2/file', b'file-id')],
 
2745
            basis=[('dir1/', b'dir-id'),
 
2746
                   ('dir1/file', b'file-id')],
 
2747
            target=[('dir2/', b'dir-id'),
 
2748
                    ('dir2/file', b'file-id')])
 
2749
        state = self.assertUpdate(  # active empty
 
2750
            active=[],
 
2751
            basis=[('dir1/', b'dir-id'),
 
2752
                   ('dir1/file', b'file-id')],
 
2753
            target=[('dir2/', b'dir-id'),
 
2754
                    ('dir2/file', b'file-id')])
 
2755
        state = self.assertUpdate(  # active present at other location
 
2756
            active=[('dir3/', b'dir-id'),
 
2757
                    ('dir3/file', b'file-id')],
 
2758
            basis=[('dir1/', b'dir-id'),
 
2759
                   ('dir1/file', b'file-id')],
 
2760
            target=[('dir2/', b'dir-id'),
 
2761
                    ('dir2/file', b'file-id')])
 
2762
        state = self.assertUpdate(  # active has different ids
 
2763
            active=[('dir1/', b'dir1-id'),
 
2764
                    ('dir1/file', b'file1-id'),
 
2765
                    ('dir2/', b'dir2-id'),
 
2766
                    ('dir2/file', b'file2-id')],
 
2767
            basis=[('dir1/', b'dir-id'),
 
2768
                   ('dir1/file', b'file-id')],
 
2769
            target=[('dir2/', b'dir-id'),
 
2770
                    ('dir2/file', b'file-id')])
 
2771
 
 
2772
    def test_invalid_file_not_present(self):
 
2773
        state = self.assertBadDelta(
 
2774
            active=[('file', b'file-id')],
 
2775
            basis=[('file', b'file-id')],
 
2776
            delta=[('other-file', 'file', b'file-id')])
 
2777
 
 
2778
    def test_invalid_new_id_same_path(self):
 
2779
        # The bad entry comes after
 
2780
        state = self.assertBadDelta(
 
2781
            active=[('file', b'file-id')],
 
2782
            basis=[('file', b'file-id')],
 
2783
            delta=[(None, 'file', b'file-id-2')])
 
2784
        # The bad entry comes first
 
2785
        state = self.assertBadDelta(
 
2786
            active=[('file', b'file-id-2')],
 
2787
            basis=[('file', b'file-id-2')],
 
2788
            delta=[(None, 'file', b'file-id')])
 
2789
 
 
2790
    def test_invalid_existing_id(self):
 
2791
        state = self.assertBadDelta(
 
2792
            active=[('file', b'file-id')],
 
2793
            basis=[('file', b'file-id')],
 
2794
            delta=[(None, 'file', b'file-id')])
 
2795
 
 
2796
    def test_invalid_parent_missing(self):
 
2797
        state = self.assertBadDelta(
 
2798
            active=[],
 
2799
            basis=[],
 
2800
            delta=[(None, 'path/path2', b'file-id')])
 
2801
        # Note: we force the active tree to have the directory, by knowing how
 
2802
        #       path_to_ie handles entries with missing parents
 
2803
        state = self.assertBadDelta(
 
2804
            active=[('path/', b'path-id')],
 
2805
            basis=[],
 
2806
            delta=[(None, 'path/path2', b'file-id')])
 
2807
        state = self.assertBadDelta(
 
2808
            active=[('path/', b'path-id'),
 
2809
                    ('path/path2', b'file-id')],
 
2810
            basis=[],
 
2811
            delta=[(None, 'path/path2', b'file-id')])
 
2812
 
 
2813
    def test_renamed_dir_same_path(self):
 
2814
        # We replace the parent directory, with another parent dir. But the C
 
2815
        # file doesn't look like it has been moved.
 
2816
        state = self.assertUpdate(  # Same as basis
 
2817
            active=[('dir/', b'A-id'),
 
2818
                    ('dir/B', b'B-id')],
 
2819
            basis=[('dir/', b'A-id'),
 
2820
                   ('dir/B', b'B-id')],
 
2821
            target=[('dir/', b'C-id'),
 
2822
                    ('dir/B', b'B-id')])
 
2823
        state = self.assertUpdate(  # Same as target
 
2824
            active=[('dir/', b'C-id'),
 
2825
                    ('dir/B', b'B-id')],
 
2826
            basis=[('dir/', b'A-id'),
 
2827
                   ('dir/B', b'B-id')],
 
2828
            target=[('dir/', b'C-id'),
 
2829
                    ('dir/B', b'B-id')])
 
2830
        state = self.assertUpdate(  # empty active
 
2831
            active=[],
 
2832
            basis=[('dir/', b'A-id'),
 
2833
                   ('dir/B', b'B-id')],
 
2834
            target=[('dir/', b'C-id'),
 
2835
                    ('dir/B', b'B-id')])
 
2836
        state = self.assertUpdate(  # different active
 
2837
            active=[('dir/', b'D-id'),
 
2838
                    ('dir/B', b'B-id')],
 
2839
            basis=[('dir/', b'A-id'),
 
2840
                   ('dir/B', b'B-id')],
 
2841
            target=[('dir/', b'C-id'),
 
2842
                    ('dir/B', b'B-id')])
 
2843
 
 
2844
    def test_parent_child_swap(self):
 
2845
        state = self.assertUpdate(  # Same as basis
 
2846
            active=[('A/', b'A-id'),
 
2847
                    ('A/B/', b'B-id'),
 
2848
                    ('A/B/C', b'C-id')],
 
2849
            basis=[('A/', b'A-id'),
 
2850
                   ('A/B/', b'B-id'),
 
2851
                   ('A/B/C', b'C-id')],
 
2852
            target=[('A/', b'B-id'),
 
2853
                    ('A/B/', b'A-id'),
 
2854
                    ('A/B/C', b'C-id')])
 
2855
        state = self.assertUpdate(  # Same as target
 
2856
            active=[('A/', b'B-id'),
 
2857
                    ('A/B/', b'A-id'),
 
2858
                    ('A/B/C', b'C-id')],
 
2859
            basis=[('A/', b'A-id'),
 
2860
                   ('A/B/', b'B-id'),
 
2861
                   ('A/B/C', b'C-id')],
 
2862
            target=[('A/', b'B-id'),
 
2863
                    ('A/B/', b'A-id'),
 
2864
                    ('A/B/C', b'C-id')])
 
2865
        state = self.assertUpdate(  # empty active
 
2866
            active=[],
 
2867
            basis=[('A/', b'A-id'),
 
2868
                   ('A/B/', b'B-id'),
 
2869
                   ('A/B/C', b'C-id')],
 
2870
            target=[('A/', b'B-id'),
 
2871
                    ('A/B/', b'A-id'),
 
2872
                    ('A/B/C', b'C-id')])
 
2873
        state = self.assertUpdate(  # different active
 
2874
            active=[('D/', b'A-id'),
 
2875
                    ('D/E/', b'B-id'),
 
2876
                    ('F', b'C-id')],
 
2877
            basis=[('A/', b'A-id'),
 
2878
                   ('A/B/', b'B-id'),
 
2879
                   ('A/B/C', b'C-id')],
 
2880
            target=[('A/', b'B-id'),
 
2881
                    ('A/B/', b'A-id'),
 
2882
                    ('A/B/C', b'C-id')])
 
2883
 
 
2884
    def test_change_root_id(self):
 
2885
        state = self.assertUpdate(  # same as basis
 
2886
            active=[('', b'root-id'),
 
2887
                    ('file', b'file-id')],
 
2888
            basis=[('', b'root-id'),
 
2889
                   ('file', b'file-id')],
 
2890
            target=[('', b'target-root-id'),
 
2891
                    ('file', b'file-id')])
 
2892
        state = self.assertUpdate(  # same as target
 
2893
            active=[('', b'target-root-id'),
 
2894
                    ('file', b'file-id')],
 
2895
            basis=[('', b'root-id'),
 
2896
                   ('file', b'file-id')],
 
2897
            target=[('', b'target-root-id'),
 
2898
                    ('file', b'root-id')])
 
2899
        state = self.assertUpdate(  # all different
 
2900
            active=[('', b'active-root-id'),
 
2901
                    ('file', b'file-id')],
 
2902
            basis=[('', b'root-id'),
 
2903
                   ('file', b'file-id')],
 
2904
            target=[('', b'target-root-id'),
 
2905
                    ('file', b'root-id')])
 
2906
 
 
2907
    def test_change_file_absent_in_active(self):
 
2908
        state = self.assertUpdate(
 
2909
            active=[],
 
2910
            basis=[('file', b'file-id')],
 
2911
            target=[('file', b'file-id')])
 
2912
 
 
2913
    def test_invalid_changed_file(self):
 
2914
        state = self.assertBadDelta(  # Not present in basis
 
2915
            active=[('file', b'file-id')],
 
2916
            basis=[],
 
2917
            delta=[('file', 'file', b'file-id')])
 
2918
        state = self.assertBadDelta(  # present at another location in basis
 
2919
            active=[('file', b'file-id')],
 
2920
            basis=[('other-file', b'file-id')],
 
2921
            delta=[('file', 'file', b'file-id')])