/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 bzrlib/tests/test_dirstate.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

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