/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/tests/test_dirstate.py

  • Committer: Martin
  • Date: 2018-07-01 10:53:23 UTC
  • mto: This revision was merged to the branch mainline in revision 7016.
  • Revision ID: gzlist@googlemail.com-20180701105323-dawmz8ngtj3qzdo7
Tweak copyright headers

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""Tests of the dirstate functionality being built for WorkingTreeFormat4."""
18
18
 
19
 
import bisect
20
19
import os
 
20
import tempfile
21
21
 
22
 
from bzrlib import (
23
 
    dirstate,
 
22
from .. import (
 
23
    controldir,
24
24
    errors,
25
 
    inventory,
26
25
    memorytree,
27
26
    osutils,
28
27
    revision as _mod_revision,
 
28
    revisiontree,
29
29
    tests,
30
30
    )
31
 
from bzrlib.tests import test_osutils
 
31
from ..bzr import (
 
32
    dirstate,
 
33
    inventory,
 
34
    inventorytree,
 
35
    workingtree_4,
 
36
    )
 
37
from . import (
 
38
    features,
 
39
    test_osutils,
 
40
    )
 
41
from .scenarios import load_tests_apply_scenarios
32
42
 
33
43
 
34
44
# TODO:
44
54
# set_path_id  setting id when state is in memory modified
45
55
 
46
56
 
47
 
def load_tests(basic_tests, module, loader):
48
 
    suite = loader.suiteClass()
49
 
    dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
50
 
        basic_tests, tests.condition_isinstance(TestCaseWithDirState))
51
 
    tests.multiply_tests(dir_reader_tests,
52
 
                         test_osutils.dir_reader_scenarios(), suite)
53
 
    suite.addTest(remaining_tests)
54
 
    return suite
 
57
class TestErrors(tests.TestCase):
 
58
 
 
59
    def test_dirstate_corrupt(self):
 
60
        error = dirstate.DirstateCorrupt('.bzr/checkout/dirstate',
 
61
                                       'trailing garbage: "x"')
 
62
        self.assertEqualDiff("The dirstate file (.bzr/checkout/dirstate)"
 
63
            " appears to be corrupt: trailing garbage: \"x\"",
 
64
            str(error))
 
65
 
 
66
 
 
67
load_tests = load_tests_apply_scenarios
55
68
 
56
69
 
57
70
class TestCaseWithDirState(tests.TestCaseWithTransport):
58
71
    """Helper functions for creating DirState objects with various content."""
59
72
 
 
73
    scenarios = test_osutils.dir_reader_scenarios()
 
74
 
60
75
    # Set by load_tests
61
76
    _dir_reader_class = None
62
77
    _native_to_unicode = None # Not used yet
63
78
 
64
79
    def setUp(self):
65
 
        tests.TestCaseWithTransport.setUp(self)
66
 
 
 
80
        super(TestCaseWithDirState, self).setUp()
67
81
        self.overrideAttr(osutils,
68
82
                          '_selected_dir_reader', self._dir_reader_class())
69
83
 
213
227
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'b-c', 'f']
214
228
        file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'b-c-id', 'f-id']
215
229
        self.build_tree(['tree/' + p for p in paths])
216
 
        tree.set_root_id('TREE_ROOT')
 
230
        tree.set_root_id(b'TREE_ROOT')
217
231
        tree.add([p.rstrip('/') for p in paths], file_ids)
218
 
        tree.commit('initial', rev_id='rev-1')
 
232
        tree.commit('initial', rev_id=b'rev-1')
219
233
        revision_id = 'rev-1'
220
234
        # a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
221
235
        t = self.get_transport('tree')
241
255
        f_len = len(f_text)
242
256
        null_stat = dirstate.DirState.NULLSTAT
243
257
        expected = {
244
 
            '':(('', '', 'TREE_ROOT'), [
 
258
            '': (('', '', 'TREE_ROOT'), [
245
259
                  ('d', '', 0, False, null_stat),
246
260
                  ('d', '', 0, False, revision_id),
247
261
                ]),
248
 
            'a':(('', 'a', 'a-id'), [
 
262
            'a': (('', 'a', 'a-id'), [
249
263
                   ('f', '', 0, False, null_stat),
250
264
                   ('f', a_sha, a_len, False, revision_id),
251
265
                 ]),
252
 
            'b':(('', 'b', 'b-id'), [
 
266
            'b': (('', 'b', 'b-id'), [
253
267
                  ('d', '', 0, False, null_stat),
254
268
                  ('d', '', 0, False, revision_id),
255
269
                 ]),
256
 
            'b/c':(('b', 'c', 'c-id'), [
 
270
            'b/c': (('b', 'c', 'c-id'), [
257
271
                    ('f', '', 0, False, null_stat),
258
272
                    ('f', c_sha, c_len, False, revision_id),
259
273
                   ]),
260
 
            'b/d':(('b', 'd', 'd-id'), [
 
274
            'b/d': (('b', 'd', 'd-id'), [
261
275
                    ('d', '', 0, False, null_stat),
262
276
                    ('d', '', 0, False, revision_id),
263
277
                   ]),
264
 
            'b/d/e':(('b/d', 'e', 'e-id'), [
 
278
            'b/d/e': (('b/d', 'e', 'e-id'), [
265
279
                      ('f', '', 0, False, null_stat),
266
280
                      ('f', e_sha, e_len, False, revision_id),
267
281
                     ]),
268
 
            'b-c':(('', 'b-c', 'b-c-id'), [
 
282
            'b-c': (('', 'b-c', 'b-c-id'), [
269
283
                      ('f', '', 0, False, null_stat),
270
284
                      ('f', b_c_sha, b_c_len, False, revision_id),
271
285
                     ]),
272
 
            'f':(('', 'f', 'f-id'), [
 
286
            'f': (('', 'f', 'f-id'), [
273
287
                  ('f', '', 0, False, null_stat),
274
288
                  ('f', f_sha, f_len, False, revision_id),
275
289
                 ]),
301
315
        tree, state, expected = self.create_basic_dirstate()
302
316
        # Now we will just remove and add every file so we get an extra entry
303
317
        # per entry. Unversion in reverse order so we handle subdirs
304
 
        tree.unversion(['f-id', 'b-c-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
 
318
        tree.unversion(['f', 'b-c', 'b/d/e', 'b/d', 'b/c', 'b', 'a'])
305
319
        tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f'],
306
320
                 ['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'b-c-id2', 'f-id2'])
307
321
 
345
359
 
346
360
        old_a = expected['a']
347
361
        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],
 
362
        expected['b/g'] = (('b', 'g', b'a-id'), [old_a[1][0],
349
363
                                                ('r', 'a', 0, False, '')])
350
364
        old_d = expected['b/d']
351
365
        expected['b/d'] = (old_d[0], [('r', 'h', 0, False, ''), old_d[1][1]])
352
 
        expected['h'] = (('', 'h', 'd-id'), [old_d[1][0],
 
366
        expected['h'] = (('', 'h', b'd-id'), [old_d[1][0],
353
367
                                             ('r', 'b/d', 0, False, '')])
354
368
 
355
369
        old_e = expected['b/d/e']
356
370
        expected['b/d/e'] = (old_e[0], [('r', 'h/e', 0, False, ''),
357
371
                             old_e[1][1]])
358
 
        expected['h/e'] = (('h', 'e', 'e-id'), [old_e[1][0],
 
372
        expected['h/e'] = (('h', 'e', b'e-id'), [old_e[1][0],
359
373
                                                ('r', 'b/d/e', 0, False, '')])
360
374
 
361
375
        state.unlock()
406
420
        # create a parent by doing a commit
407
421
        tree = self.make_branch_and_tree('tree')
408
422
        rev_id = tree.commit('first post')
409
 
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
423
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
410
424
        rev_id2 = tree2.commit('second post', allow_pointless=True)
411
425
        tree.merge_from_branch(tree2.branch)
412
426
        expected_result = ([rev_id, rev_id2], [
438
452
    def get_tree_with_a_file(self):
439
453
        tree = self.make_branch_and_tree('tree')
440
454
        self.build_tree(['tree/a file'])
441
 
        tree.add('a file', 'a-file-id')
 
455
        tree.add('a file', b'a-file-id')
442
456
        return tree
443
457
 
444
458
    def test_non_empty_no_parents_to_dirstate(self):
449
463
            (('', '', tree.get_root_id()), # common details
450
464
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
451
465
             ]),
452
 
            (('', 'a file', 'a-file-id'), # common
 
466
            (('', 'a file', b'a-file-id'), # common
453
467
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
454
468
             ]),
455
469
            ])
462
476
        rev_id = tree.commit('first post').encode('utf8')
463
477
        # change the current content to be different this will alter stat, sha
464
478
        # and length:
465
 
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
479
        self.build_tree_contents([('tree/a file', b'new content\n')])
466
480
        expected_result = ([rev_id], [
467
481
            (('', '', tree.get_root_id()), # common details
468
482
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
469
483
              ('d', '', 0, False, rev_id), # first parent details
470
484
             ]),
471
 
            (('', 'a file', 'a-file-id'), # common
 
485
            (('', 'a file', b'a-file-id'), # common
472
486
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
473
487
              ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
474
488
               rev_id), # first parent
481
495
        # create a parent by doing a commit
482
496
        tree = self.get_tree_with_a_file()
483
497
        rev_id = tree.commit('first post').encode('utf8')
484
 
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
498
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
485
499
        # change the current content to be different this will alter stat, sha
486
500
        # and length:
487
501
        self.build_tree_contents([('tree2/a file', 'merge content\n')])
489
503
        tree.merge_from_branch(tree2.branch)
490
504
        # change the current content to be different this will alter stat, sha
491
505
        # and length again, giving us three distinct values:
492
 
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
506
        self.build_tree_contents([('tree/a file', b'new content\n')])
493
507
        expected_result = ([rev_id, rev_id2], [
494
508
            (('', '', tree.get_root_id()), # common details
495
509
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
496
510
              ('d', '', 0, False, rev_id), # first parent details
497
511
              ('d', '', 0, False, rev_id), # second parent details
498
512
             ]),
499
 
            (('', 'a file', 'a-file-id'), # common
 
513
            (('', 'a file', b'a-file-id'), # common
500
514
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
501
515
              ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
502
516
               rev_id), # first parent
532
546
 
533
547
class TestDirStateOnFile(TestCaseWithDirState):
534
548
 
 
549
    def create_updated_dirstate(self):
 
550
        self.build_tree(['a-file'])
 
551
        tree = self.make_branch_and_tree('.')
 
552
        tree.add(['a-file'], [b'a-id'])
 
553
        tree.commit('add a-file')
 
554
        # Save and unlock the state, re-open it in readonly mode
 
555
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
556
        state.save()
 
557
        state.unlock()
 
558
        state = dirstate.DirState.on_file('dirstate')
 
559
        state.lock_read()
 
560
        return state
 
561
 
535
562
    def test_construct_with_path(self):
536
563
        tree = self.make_branch_and_tree('tree')
537
564
        state = dirstate.DirState.from_tree(tree, 'dirstate.from_tree')
539
566
        # write to disk.
540
567
        lines = state.get_lines()
541
568
        state.unlock()
542
 
        self.build_tree_contents([('dirstate', ''.join(lines))])
 
569
        self.build_tree_contents([('dirstate', b''.join(lines))])
543
570
        # get a state object
544
571
        # no parents, default tree content
545
572
        expected_result = ([], [
566
593
            state.unlock()
567
594
 
568
595
    def test_can_save_in_read_lock(self):
569
 
        self.build_tree(['a-file'])
570
 
        state = dirstate.DirState.initialize('dirstate')
571
 
        try:
572
 
            # No stat and no sha1 sum.
573
 
            state.add('a-file', 'a-file-id', 'file', None, '')
574
 
            state.save()
575
 
        finally:
576
 
            state.unlock()
577
 
 
578
 
        # Now open in readonly mode
579
 
        state = dirstate.DirState.on_file('dirstate')
580
 
        state.lock_read()
 
596
        state = self.create_updated_dirstate()
581
597
        try:
582
598
            entry = state._get_entry(0, path_utf8='a-file')
583
599
            # The current size should be 0 (default)
584
600
            self.assertEqual(0, entry[1][0][2])
585
601
            # We should have a real entry.
586
602
            self.assertNotEqual((None, None), entry)
587
 
            # Make sure everything is old enough
 
603
            # Set the cutoff-time into the future, so things look cacheable
588
604
            state._sha_cutoff_time()
589
 
            state._cutoff_time += 10
590
 
            # Change the file length
591
 
            self.build_tree_contents([('a-file', 'shorter')])
592
 
            sha1sum = dirstate.update_entry(state, entry, 'a-file',
593
 
                os.lstat('a-file'))
594
 
            # new file, no cached sha:
595
 
            self.assertEqual(None, sha1sum)
 
605
            state._cutoff_time += 10.0
 
606
            st = os.lstat('a-file')
 
607
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
608
            # We updated the current sha1sum because the file is cacheable
 
609
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
610
                             sha1sum)
596
611
 
597
612
            # The dirblock has been updated
598
 
            self.assertEqual(7, entry[1][0][2])
599
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
613
            self.assertEqual(st.st_size, entry[1][0][2])
 
614
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
600
615
                             state._dirblock_state)
601
616
 
602
617
            del entry
611
626
        state.lock_read()
612
627
        try:
613
628
            entry = state._get_entry(0, path_utf8='a-file')
614
 
            self.assertEqual(7, entry[1][0][2])
 
629
            self.assertEqual(st.st_size, entry[1][0][2])
615
630
        finally:
616
631
            state.unlock()
617
632
 
618
633
    def test_save_fails_quietly_if_locked(self):
619
634
        """If dirstate is locked, save will fail without complaining."""
620
 
        self.build_tree(['a-file'])
621
 
        state = dirstate.DirState.initialize('dirstate')
622
 
        try:
623
 
            # No stat and no sha1 sum.
624
 
            state.add('a-file', 'a-file-id', 'file', None, '')
625
 
            state.save()
626
 
        finally:
627
 
            state.unlock()
628
 
 
629
 
        state = dirstate.DirState.on_file('dirstate')
630
 
        state.lock_read()
 
635
        state = self.create_updated_dirstate()
631
636
        try:
632
637
            entry = state._get_entry(0, path_utf8='a-file')
633
 
            sha1sum = dirstate.update_entry(state, entry, 'a-file',
634
 
                os.lstat('a-file'))
635
 
            # No sha - too new
636
 
            self.assertEqual(None, sha1sum)
637
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
638
            # No cached sha1 yet.
 
639
            self.assertEqual('', entry[1][0][1])
 
640
            # Set the cutoff-time into the future, so things look cacheable
 
641
            state._sha_cutoff_time()
 
642
            state._cutoff_time += 10.0
 
643
            st = os.lstat('a-file')
 
644
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
645
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
646
                             sha1sum)
 
647
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
638
648
                             state._dirblock_state)
639
649
 
640
650
            # Now, before we try to save, grab another dirstate, and take out a
671
681
        state = dirstate.DirState.initialize('dirstate')
672
682
        try:
673
683
            # No stat and no sha1 sum.
674
 
            state.add('a-file', 'a-file-id', 'file', None, '')
 
684
            state.add('a-file', b'a-file-id', 'file', None, '')
675
685
            state.save()
676
686
        finally:
677
687
            state.unlock()
678
688
 
679
689
        # The dirstate should include TREE_ROOT and 'a-file' and nothing else
680
690
        expected_blocks = [
681
 
            ('', [(('', '', 'TREE_ROOT'),
 
691
            ('', [(('', '', b'TREE_ROOT'),
682
692
                   [('d', '', 0, False, dirstate.DirState.NULLSTAT)])]),
683
 
            ('', [(('', 'a-file', 'a-file-id'),
 
693
            ('', [(('', 'a-file', b'a-file-id'),
684
694
                   [('f', '', 0, False, dirstate.DirState.NULLSTAT)])]),
685
695
        ]
686
696
 
691
701
            self.assertEqual(expected_blocks, state._dirblocks)
692
702
 
693
703
            # Now modify the state, but mark it as inconsistent
694
 
            state.add('a-dir', 'a-dir-id', 'directory', None, '')
 
704
            state.add(b'a-dir', b'a-dir-id', 'directory', None, '')
695
705
            state._changes_aborted = True
696
706
            state.save()
697
707
        finally:
723
733
        # On win32 you can't read from a locked file, even within the same
724
734
        # process. So we have to unlock and release before we check the file
725
735
        # contents.
726
 
        self.assertFileEqual(''.join(lines), 'dirstate')
 
736
        self.assertFileEqual(b''.join(lines), 'dirstate')
727
737
        state.lock_read() # check_state_with_reopen will unlock
728
738
        self.check_state_with_reopen(expected_result, state)
729
739
 
730
740
 
731
741
class TestDirStateManipulations(TestCaseWithDirState):
732
742
 
 
743
    def make_minimal_tree(self):
 
744
        tree1 = self.make_branch_and_memory_tree('tree1')
 
745
        tree1.lock_write()
 
746
        self.addCleanup(tree1.unlock)
 
747
        tree1.add('')
 
748
        revid1 = tree1.commit('foo')
 
749
        return tree1, revid1
 
750
 
 
751
    def test_update_minimal_updates_id_index(self):
 
752
        state = self.create_dirstate_with_root_and_subdir()
 
753
        self.addCleanup(state.unlock)
 
754
        id_index = state._get_id_index()
 
755
        self.assertEqual(['a-root-value', b'subdir-id'], sorted(id_index))
 
756
        state.add('file-name', b'file-id', 'file', None, '')
 
757
        self.assertEqual(['a-root-value', b'file-id', b'subdir-id'],
 
758
                         sorted(id_index))
 
759
        state.update_minimal(('', 'new-name', b'file-id'), 'f',
 
760
                             path_utf8='new-name')
 
761
        self.assertEqual(['a-root-value', b'file-id', b'subdir-id'],
 
762
                         sorted(id_index))
 
763
        self.assertEqual([('', 'new-name', b'file-id')],
 
764
                         sorted(id_index[b'file-id']))
 
765
        state._validate()
 
766
 
733
767
    def test_set_state_from_inventory_no_content_no_parents(self):
734
768
        # setting the current inventory is a slow but important api to support.
735
 
        tree1 = self.make_branch_and_memory_tree('tree1')
736
 
        tree1.lock_write()
737
 
        try:
738
 
            tree1.add('')
739
 
            revid1 = tree1.commit('foo').encode('utf8')
740
 
            root_id = tree1.get_root_id()
741
 
            inv = tree1.inventory
742
 
        finally:
743
 
            tree1.unlock()
 
769
        tree1, revid1 = self.make_minimal_tree()
 
770
        inv = tree1.root_inventory
 
771
        root_id = inv.path2id('')
744
772
        expected_result = [], [
745
773
            (('', '', root_id), [
746
774
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
758
786
            # This will unlock it
759
787
            self.check_state_with_reopen(expected_result, state)
760
788
 
 
789
    def test_set_state_from_scratch_no_parents(self):
 
790
        tree1, revid1 = self.make_minimal_tree()
 
791
        inv = tree1.root_inventory
 
792
        root_id = inv.path2id('')
 
793
        expected_result = [], [
 
794
            (('', '', root_id), [
 
795
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
 
796
        state = dirstate.DirState.initialize('dirstate')
 
797
        try:
 
798
            state.set_state_from_scratch(inv, [], [])
 
799
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
800
                             state._header_state)
 
801
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
802
                             state._dirblock_state)
 
803
        except:
 
804
            state.unlock()
 
805
            raise
 
806
        else:
 
807
            # This will unlock it
 
808
            self.check_state_with_reopen(expected_result, state)
 
809
 
 
810
    def test_set_state_from_scratch_identical_parent(self):
 
811
        tree1, revid1 = self.make_minimal_tree()
 
812
        inv = tree1.root_inventory
 
813
        root_id = inv.path2id('')
 
814
        rev_tree1 = tree1.branch.repository.revision_tree(revid1)
 
815
        d_entry = ('d', '', 0, False, dirstate.DirState.NULLSTAT)
 
816
        parent_entry = ('d', '', 0, False, revid1)
 
817
        expected_result = [revid1], [
 
818
            (('', '', root_id), [d_entry, parent_entry])]
 
819
        state = dirstate.DirState.initialize('dirstate')
 
820
        try:
 
821
            state.set_state_from_scratch(inv, [(revid1, rev_tree1)], [])
 
822
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
823
                             state._header_state)
 
824
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
825
                             state._dirblock_state)
 
826
        except:
 
827
            state.unlock()
 
828
            raise
 
829
        else:
 
830
            # This will unlock it
 
831
            self.check_state_with_reopen(expected_result, state)
 
832
 
761
833
    def test_set_state_from_inventory_preserves_hashcache(self):
762
834
        # https://bugs.launchpad.net/bzr/+bug/146176
763
835
        # set_state_from_inventory should preserve the stat and hash value for
769
841
        try:
770
842
            # make a dirstate with some valid hashcache data
771
843
            # file on disk, but that's not needed for this test
772
 
            foo_contents = 'contents of foo'
 
844
            foo_contents = b'contents of foo'
773
845
            self.build_tree_contents([('foo', foo_contents)])
774
 
            tree.add('foo', 'foo-id')
 
846
            tree.add('foo', b'foo-id')
775
847
 
776
848
            foo_stat = os.stat('foo')
777
849
            foo_packed = dirstate.pack_stat(foo_stat)
795
867
                tree._dirstate._get_entry(0, 'foo-id'))
796
868
 
797
869
            # extract the inventory, and add something to it
798
 
            inv = tree._get_inventory()
 
870
            inv = tree._get_root_inventory()
799
871
            # should see the file we poked in...
800
 
            self.assertTrue(inv.has_id('foo-id'))
 
872
            self.assertTrue(inv.has_id(b'foo-id'))
801
873
            self.assertTrue(inv.has_filename('foo'))
802
 
            inv.add_path('bar', 'file', 'bar-id')
 
874
            inv.add_path('bar', 'file', b'bar-id')
803
875
            tree._dirstate._validate()
804
876
            # this used to cause it to lose its hashcache
805
877
            tree._dirstate.set_state_from_inventory(inv)
829
901
        try:
830
902
            tree1.add(['a', 'a/b', 'a-b', 'a/b/foo', 'a-b/bar'],
831
903
                      ['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
832
 
            tree1.commit('rev1', rev_id='rev1')
 
904
            tree1.commit('rev1', rev_id=b'rev1')
833
905
            root_id = tree1.get_root_id()
834
 
            inv = tree1.inventory
 
906
            inv = tree1.root_inventory
835
907
        finally:
836
908
            tree1.unlock()
837
909
        expected_result1 = [('', '', root_id, 'd'),
853
925
            for entry in state._iter_entries():
854
926
                values.append(entry[0] + entry[1][0][:1])
855
927
            self.assertEqual(expected_result1, values)
856
 
            del inv['b-id']
 
928
            inv.delete('b-id')
857
929
            state.set_state_from_inventory(inv)
858
930
            values = []
859
931
            for entry in state._iter_entries():
900
972
        """Set the root file id in a dirstate with parents"""
901
973
        mt = self.make_branch_and_tree('mt')
902
974
        # in case the default tree format uses a different root id
903
 
        mt.set_root_id('TREE_ROOT')
904
 
        mt.commit('foo', rev_id='parent-revid')
905
 
        rt = mt.branch.repository.revision_tree('parent-revid')
 
975
        mt.set_root_id(b'TREE_ROOT')
 
976
        mt.commit('foo', rev_id=b'parent-revid')
 
977
        rt = mt.branch.repository.revision_tree(b'parent-revid')
906
978
        state = dirstate.DirState.initialize('dirstate')
907
979
        state._validate()
908
980
        try:
967
1039
            revid1 = tree1.commit('foo')
968
1040
        finally:
969
1041
            tree1.unlock()
970
 
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
 
1042
        branch2 = tree1.branch.controldir.clone('tree2').open_branch()
971
1043
        tree2 = memorytree.MemoryTree.create_on_branch(branch2)
972
1044
        tree2.lock_write()
973
1045
        try:
1032
1104
        tree1.lock_write()
1033
1105
        try:
1034
1106
            tree1.add('')
1035
 
            tree1.add(['a file'], ['file-id'], ['file'])
1036
 
            tree1.put_file_bytes_non_atomic('file-id', 'file-content')
 
1107
            tree1.add(['a file'], [b'file-id'], ['file'])
 
1108
            tree1.put_file_bytes_non_atomic('a file', b'file-content')
1037
1109
            revid1 = tree1.commit('foo')
1038
1110
        finally:
1039
1111
            tree1.unlock()
1040
 
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
 
1112
        branch2 = tree1.branch.controldir.clone('tree2').open_branch()
1041
1113
        tree2 = memorytree.MemoryTree.create_on_branch(branch2)
1042
1114
        tree2.lock_write()
1043
1115
        try:
1044
 
            tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
 
1116
            tree2.put_file_bytes_non_atomic('a file', b'new file-content')
1045
1117
            revid2 = tree2.commit('foo')
1046
1118
            root_id = tree2.get_root_id()
1047
1119
        finally:
1053
1125
             ('d', '', 0, False, revid1.encode('utf8')),
1054
1126
             ('d', '', 0, False, revid1.encode('utf8'))
1055
1127
             ]),
1056
 
            (('', 'a file', 'file-id'), [
 
1128
            (('', 'a file', b'file-id'), [
1057
1129
             ('a', '', 0, False, ''),
1058
1130
             ('f', '2439573625385400f2a669657a7db6ae7515d371', 12, False,
1059
1131
              revid1.encode('utf8')),
1086
1158
        # the 1*20 is the sha1 pretend value.
1087
1159
        state = dirstate.DirState.initialize('dirstate')
1088
1160
        expected_entries = [
1089
 
            (('', '', 'TREE_ROOT'), [
 
1161
            (('', '', b'TREE_ROOT'), [
1090
1162
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
1091
1163
             ]),
1092
 
            (('', 'a file', 'a-file-id'), [
 
1164
            (('', 'a file', b'a-file-id'), [
1093
1165
             ('f', '1'*20, 19, False, dirstate.pack_stat(stat)), # current tree
1094
1166
             ]),
1095
1167
            ]
1096
1168
        try:
1097
 
            state.add('a file', 'a-file-id', 'file', stat, '1'*20)
 
1169
            state.add('a file', b'a-file-id', 'file', stat, '1'*20)
1098
1170
            # having added it, it should be in the output of iter_entries.
1099
1171
            self.assertEqual(expected_entries, list(state._iter_entries()))
1100
1172
            # saving and reloading should not affect this.
1151
1223
        # The most trivial addition of a symlink when there are no parents and
1152
1224
        # its in the root and all data about the file is supplied
1153
1225
        # bzr doesn't support fake symlinks on windows, yet.
1154
 
        self.requireFeature(tests.SymlinkFeature)
 
1226
        self.requireFeature(features.SymlinkFeature)
1155
1227
        os.symlink(target, link_name)
1156
1228
        stat = os.lstat(link_name)
1157
1229
        expected_entries = [
1182
1254
        self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
1183
1255
 
1184
1256
    def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1185
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
1257
        self.requireFeature(features.UnicodeFilenameFeature)
1186
1258
        self._test_add_symlink_to_root_no_parents_all_data(
1187
1259
            u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1188
1260
 
1205
1277
            ]
1206
1278
        state = dirstate.DirState.initialize('dirstate')
1207
1279
        try:
1208
 
            state.add('a dir', 'a dir id', 'directory', dirstat, None)
1209
 
            state.add('a dir/a file', 'a-file-id', 'file', filestat, '1'*20)
 
1280
            state.add('a dir', b'a dir id', 'directory', dirstat, None)
 
1281
            state.add('a dir/a file', b'a-file-id', 'file', filestat, '1'*20)
1210
1282
            # added it, it should be in the output of iter_entries.
1211
1283
            self.assertEqual(expected_entries, list(state._iter_entries()))
1212
1284
            # saving and reloading should not affect this.
1222
1294
        # make a dirstate and add a tree reference
1223
1295
        state = dirstate.DirState.initialize('dirstate')
1224
1296
        expected_entry = (
1225
 
            ('', 'subdir', 'subdir-id'),
 
1297
            ('', 'subdir', b'subdir-id'),
1226
1298
            [('t', 'subtree-123123', 0, False,
1227
1299
              'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')],
1228
1300
            )
1229
1301
        try:
1230
 
            state.add('subdir', 'subdir-id', 'tree-reference', None, 'subtree-123123')
1231
 
            entry = state._get_entry(0, 'subdir-id', 'subdir')
 
1302
            state.add('subdir', b'subdir-id', 'tree-reference', None, 'subtree-123123')
 
1303
            entry = state._get_entry(0, b'subdir-id', 'subdir')
1232
1304
            self.assertEqual(entry, expected_entry)
1233
1305
            state._validate()
1234
1306
            state.save()
1238
1310
        state.lock_read()
1239
1311
        self.addCleanup(state.unlock)
1240
1312
        state._validate()
1241
 
        entry2 = state._get_entry(0, 'subdir-id', 'subdir')
 
1313
        entry2 = state._get_entry(0, b'subdir-id', 'subdir')
1242
1314
        self.assertEqual(entry, entry2)
1243
1315
        self.assertEqual(entry, expected_entry)
1244
1316
        # and lookup by id should work too
1245
 
        entry2 = state._get_entry(0, fileid_utf8='subdir-id')
 
1317
        entry2 = state._get_entry(0, fileid_utf8=b'subdir-id')
1246
1318
        self.assertEqual(entry, expected_entry)
1247
1319
 
1248
1320
    def test_add_forbidden_names(self):
1263
1335
        self.build_tree(['tree1/b'])
1264
1336
        tree1.lock_write()
1265
1337
        try:
1266
 
            tree1.add(['b'], ['b-id'])
 
1338
            tree1.add(['b'], [b'b-id'])
1267
1339
            root_id = tree1.get_root_id()
1268
 
            inv = tree1.inventory
 
1340
            inv = tree1.root_inventory
1269
1341
            state = dirstate.DirState.initialize('dirstate')
1270
1342
            try:
1271
1343
                # Set the initial state with 'b'
1286
1358
            tree1.unlock()
1287
1359
 
1288
1360
 
 
1361
class TestDirStateHashUpdates(TestCaseWithDirState):
 
1362
 
 
1363
    def do_update_entry(self, state, path):
 
1364
        entry = state._get_entry(0, path_utf8=path)
 
1365
        stat = os.lstat(path)
 
1366
        return dirstate.update_entry(state, entry, os.path.abspath(path), stat)
 
1367
 
 
1368
    def _read_state_content(self, state):
 
1369
        """Read the content of the dirstate file.
 
1370
 
 
1371
        On Windows when one process locks a file, you can't even open() the
 
1372
        file in another process (to read it). So we go directly to
 
1373
        state._state_file. This should always be the exact disk representation,
 
1374
        so it is reasonable to do so.
 
1375
        DirState also always seeks before reading, so it doesn't matter if we
 
1376
        bump the file pointer.
 
1377
        """
 
1378
        state._state_file.seek(0)
 
1379
        return state._state_file.read()
 
1380
 
 
1381
    def test_worth_saving_limit_avoids_writing(self):
 
1382
        tree = self.make_branch_and_tree('.')
 
1383
        self.build_tree(['c', 'd'])
 
1384
        tree.lock_write()
 
1385
        tree.add(['c', 'd'], [b'c-id', b'd-id'])
 
1386
        tree.commit('add c and d')
 
1387
        state = InstrumentedDirState.on_file(tree.current_dirstate()._filename,
 
1388
                                             worth_saving_limit=2)
 
1389
        tree.unlock()
 
1390
        state.lock_write()
 
1391
        self.addCleanup(state.unlock)
 
1392
        state._read_dirblocks_if_needed()
 
1393
        state.adjust_time(+20) # Allow things to be cached
 
1394
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1395
                         state._dirblock_state)
 
1396
        content = self._read_state_content(state)
 
1397
        self.do_update_entry(state, b'c')
 
1398
        self.assertEqual(1, len(state._known_hash_changes))
 
1399
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1400
                         state._dirblock_state)
 
1401
        state.save()
 
1402
        # It should not have set the state to IN_MEMORY_UNMODIFIED because the
 
1403
        # hash values haven't been written out.
 
1404
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1405
                         state._dirblock_state)
 
1406
        self.assertEqual(content, self._read_state_content(state))
 
1407
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1408
                         state._dirblock_state)
 
1409
        self.do_update_entry(state, b'd')
 
1410
        self.assertEqual(2, len(state._known_hash_changes))
 
1411
        state.save()
 
1412
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1413
                         state._dirblock_state)
 
1414
        self.assertEqual(0, len(state._known_hash_changes))
 
1415
 
 
1416
 
1289
1417
class TestGetLines(TestCaseWithDirState):
1290
1418
 
1291
1419
    def test_get_line_with_2_rows(self):
1684
1812
class InstrumentedDirState(dirstate.DirState):
1685
1813
    """An DirState with instrumented sha1 functionality."""
1686
1814
 
1687
 
    def __init__(self, path, sha1_provider):
1688
 
        super(InstrumentedDirState, self).__init__(path, sha1_provider)
 
1815
    def __init__(self, path, sha1_provider, worth_saving_limit=0):
 
1816
        super(InstrumentedDirState, self).__init__(path, sha1_provider,
 
1817
            worth_saving_limit=worth_saving_limit)
1689
1818
        self._time_offset = 0
1690
1819
        self._log = []
1691
1820
        # member is dynamically set in DirState.__init__ to turn on trace
1748
1877
        self.assertEqual(expected, dirstate.pack_stat(stat_value))
1749
1878
 
1750
1879
    def test_pack_stat_int(self):
1751
 
        st = _FakeStat(6859L, 1172758614, 1172758617, 777L, 6499538L, 0100644)
 
1880
        st = _FakeStat(6859, 1172758614, 1172758617, 777, 6499538, 0o100644)
1752
1881
        # Make sure that all parameters have an impact on the packed stat.
1753
1882
        self.assertPackStat('AAAay0Xm4FZF5uBZAAADCQBjLNIAAIGk', st)
1754
 
        st.st_size = 7000L
 
1883
        st.st_size = 7000
1755
1884
        #                ay0 => bWE
1756
1885
        self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1757
1886
        st.st_mtime = 1172758620
1760
1889
        st.st_ctime = 1172758630
1761
1890
        #                          uBZ => uBm
1762
1891
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1763
 
        st.st_dev = 888L
 
1892
        st.st_dev = 888
1764
1893
        #                                DCQ => DeA
1765
1894
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNIAAIGk', st)
1766
 
        st.st_ino = 6499540L
 
1895
        st.st_ino = 6499540
1767
1896
        #                                     LNI => LNQ
1768
1897
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIGk', st)
1769
 
        st.st_mode = 0100744
 
1898
        st.st_mode = 0o100744
1770
1899
        #                                          IGk => IHk
1771
1900
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIHk', st)
1772
1901
 
1776
1905
        Make sure we don't get warnings or errors, and that we ignore changes <
1777
1906
        1s
1778
1907
        """
1779
 
        st = _FakeStat(7000L, 1172758614.0, 1172758617.0,
1780
 
                       777L, 6499538L, 0100644)
 
1908
        st = _FakeStat(7000, 1172758614.0, 1172758617.0,
 
1909
                       777, 6499538, 0o100644)
1781
1910
        # These should all be the same as the integer counterparts
1782
1911
        self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1783
1912
        st.st_mtime = 1172758620.0
1898
2027
        """Test bisect when there is only 1 page to read"""
1899
2028
        tree, state, expected = self.create_basic_dirstate()
1900
2029
        state._bisect_page_size = 5000
1901
 
        self.assertBisect(expected,[['']], state, [''])
1902
 
        self.assertBisect(expected,[['a']], state, ['a'])
1903
 
        self.assertBisect(expected,[['b']], state, ['b'])
1904
 
        self.assertBisect(expected,[['b/c']], state, ['b/c'])
1905
 
        self.assertBisect(expected,[['b/d']], state, ['b/d'])
1906
 
        self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
1907
 
        self.assertBisect(expected,[['b-c']], state, ['b-c'])
1908
 
        self.assertBisect(expected,[['f']], state, ['f'])
1909
 
        self.assertBisect(expected,[['a'], ['b'], ['f']],
 
2030
        self.assertBisect(expected, [['']], state, [''])
 
2031
        self.assertBisect(expected, [['a']], state, ['a'])
 
2032
        self.assertBisect(expected, [['b']], state, ['b'])
 
2033
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
 
2034
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
 
2035
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
 
2036
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
 
2037
        self.assertBisect(expected, [['f']], state, ['f'])
 
2038
        self.assertBisect(expected, [['a'], ['b'], ['f']],
1910
2039
                          state, ['a', 'b', 'f'])
1911
2040
        self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
1912
2041
                          state, ['b/d', 'b/d/e', 'f'])
2092
2221
class TestDirstateTreeReference(TestCaseWithDirState):
2093
2222
 
2094
2223
    def test_reference_revision_is_none(self):
2095
 
        tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
 
2224
        tree = self.make_branch_and_tree('tree', format='development-subtree')
2096
2225
        subtree = self.make_branch_and_tree('tree/subtree',
2097
 
                            format='dirstate-with-subtree')
2098
 
        subtree.set_root_id('subtree')
 
2226
                            format='development-subtree')
 
2227
        subtree.set_root_id(b'subtree')
2099
2228
        tree.add_reference(subtree)
2100
2229
        tree.add('subtree')
2101
2230
        state = dirstate.DirState.from_tree(tree, 'dirstate')
2287
2416
        self.assertIsInstance(tree_data, str)
2288
2417
 
2289
2418
    def test_unicode_symlink(self):
2290
 
        inv_entry = inventory.InventoryLink('link-file-id',
 
2419
        inv_entry = inventory.InventoryLink(b'link-file-id',
2291
2420
                                            u'nam\N{Euro Sign}e',
2292
 
                                            'link-parent-id')
 
2421
                                            b'link-parent-id')
2293
2422
        inv_entry.revision = 'link-revision-id'
2294
2423
        target = u'link-targ\N{Euro Sign}t'
2295
2424
        inv_entry.symlink_target = target
2305
2434
        self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2306
2435
 
2307
2436
    def test_defaultsha1provider_sha1(self):
2308
 
        text = 'test\r\nwith\nall\rpossible line endings\r\n'
 
2437
        text = b'test\r\nwith\nall\rpossible line endings\r\n'
2309
2438
        self.build_tree_contents([('foo', text)])
2310
2439
        expected_sha = osutils.sha_string(text)
2311
2440
        p = dirstate.DefaultSHA1Provider()
2312
2441
        self.assertEqual(expected_sha, p.sha1('foo'))
2313
2442
 
2314
2443
    def test_defaultsha1provider_stat_and_sha1(self):
2315
 
        text = 'test\r\nwith\nall\rpossible line endings\r\n'
 
2444
        text = b'test\r\nwith\nall\rpossible line endings\r\n'
2316
2445
        self.build_tree_contents([('foo', text)])
2317
2446
        expected_sha = osutils.sha_string(text)
2318
2447
        p = dirstate.DefaultSHA1Provider()
2320
2449
        self.assertTrue(len(statvalue) >= 10)
2321
2450
        self.assertEqual(len(text), statvalue.st_size)
2322
2451
        self.assertEqual(expected_sha, sha1)
 
2452
 
 
2453
 
 
2454
class _Repo(object):
 
2455
    """A minimal api to get InventoryRevisionTree to work."""
 
2456
 
 
2457
    def __init__(self):
 
2458
        default_format = controldir.format_registry.make_controldir('default')
 
2459
        self._format = default_format.repository_format
 
2460
 
 
2461
    def lock_read(self):
 
2462
        pass
 
2463
 
 
2464
    def unlock(self):
 
2465
        pass
 
2466
 
 
2467
 
 
2468
class TestUpdateBasisByDelta(tests.TestCase):
 
2469
 
 
2470
    def path_to_ie(self, path, file_id, rev_id, dir_ids):
 
2471
        if path.endswith('/'):
 
2472
            is_dir = True
 
2473
            path = path[:-1]
 
2474
        else:
 
2475
            is_dir = False
 
2476
        dirname, basename = osutils.split(path)
 
2477
        try:
 
2478
            dir_id = dir_ids[dirname]
 
2479
        except KeyError:
 
2480
            dir_id = osutils.basename(dirname) + '-id'
 
2481
        if is_dir:
 
2482
            ie = inventory.InventoryDirectory(file_id, basename, dir_id)
 
2483
            dir_ids[path] = file_id
 
2484
        else:
 
2485
            ie = inventory.InventoryFile(file_id, basename, dir_id)
 
2486
            ie.text_size = 0
 
2487
            ie.text_sha1 = ''
 
2488
        ie.revision = rev_id
 
2489
        return ie
 
2490
 
 
2491
    def create_tree_from_shape(self, rev_id, shape):
 
2492
        dir_ids = {'': 'root-id'}
 
2493
        inv = inventory.Inventory('root-id', rev_id)
 
2494
        for info in shape:
 
2495
            if len(info) == 2:
 
2496
                path, file_id = info
 
2497
                ie_rev_id = rev_id
 
2498
            else:
 
2499
                path, file_id, ie_rev_id = info
 
2500
            if path == '':
 
2501
                # Replace the root entry
 
2502
                del inv._byid[inv.root.file_id]
 
2503
                inv.root.file_id = file_id
 
2504
                inv._byid[file_id] = inv.root
 
2505
                dir_ids[''] = file_id
 
2506
                continue
 
2507
            inv.add(self.path_to_ie(path, file_id, ie_rev_id, dir_ids))
 
2508
        return inventorytree.InventoryRevisionTree(_Repo(), inv, rev_id)
 
2509
 
 
2510
    def create_empty_dirstate(self):
 
2511
        fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
 
2512
        self.addCleanup(os.remove, path)
 
2513
        os.close(fd)
 
2514
        state = dirstate.DirState.initialize(path)
 
2515
        self.addCleanup(state.unlock)
 
2516
        return state
 
2517
 
 
2518
    def create_inv_delta(self, delta, rev_id):
 
2519
        """Translate a 'delta shape' into an actual InventoryDelta"""
 
2520
        dir_ids = {'': 'root-id'}
 
2521
        inv_delta = []
 
2522
        for old_path, new_path, file_id in delta:
 
2523
            if old_path is not None and old_path.endswith('/'):
 
2524
                # Don't have to actually do anything for this, because only
 
2525
                # new_path creates InventoryEntries
 
2526
                old_path = old_path[:-1]
 
2527
            if new_path is None: # Delete
 
2528
                inv_delta.append((old_path, None, file_id, None))
 
2529
                continue
 
2530
            ie = self.path_to_ie(new_path, file_id, rev_id, dir_ids)
 
2531
            inv_delta.append((old_path, new_path, file_id, ie))
 
2532
        return inv_delta
 
2533
 
 
2534
    def assertUpdate(self, active, basis, target):
 
2535
        """Assert that update_basis_by_delta works how we want.
 
2536
 
 
2537
        Set up a DirState object with active_shape for tree 0, basis_shape for
 
2538
        tree 1. Then apply the delta from basis_shape to target_shape,
 
2539
        and assert that the DirState is still valid, and that its stored
 
2540
        content matches the target_shape.
 
2541
        """
 
2542
        active_tree = self.create_tree_from_shape('active', active)
 
2543
        basis_tree = self.create_tree_from_shape('basis', basis)
 
2544
        target_tree = self.create_tree_from_shape('target', target)
 
2545
        state = self.create_empty_dirstate()
 
2546
        state.set_state_from_scratch(active_tree.root_inventory,
 
2547
            [('basis', basis_tree)], [])
 
2548
        delta = target_tree.root_inventory._make_delta(
 
2549
            basis_tree.root_inventory)
 
2550
        state.update_basis_by_delta(delta, 'target')
 
2551
        state._validate()
 
2552
        dirstate_tree = workingtree_4.DirStateRevisionTree(state,
 
2553
            'target', _Repo())
 
2554
        # The target now that delta has been applied should match the
 
2555
        # RevisionTree
 
2556
        self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
 
2557
        # And the dirblock state should be identical to the state if we created
 
2558
        # it from scratch.
 
2559
        state2 = self.create_empty_dirstate()
 
2560
        state2.set_state_from_scratch(active_tree.root_inventory,
 
2561
            [('target', target_tree)], [])
 
2562
        self.assertEqual(state2._dirblocks, state._dirblocks)
 
2563
        return state
 
2564
 
 
2565
    def assertBadDelta(self, active, basis, delta):
 
2566
        """Test that we raise InconsistentDelta when appropriate.
 
2567
 
 
2568
        :param active: The active tree shape
 
2569
        :param basis: The basis tree shape
 
2570
        :param delta: A description of the delta to apply. Similar to the form
 
2571
            for regular inventory deltas, but omitting the InventoryEntry.
 
2572
            So adding a file is: (None, 'path', b'file-id')
 
2573
            Adding a directory is: (None, 'path/', 'dir-id')
 
2574
            Renaming a dir is: ('old/', 'new/', 'dir-id')
 
2575
            etc.
 
2576
        """
 
2577
        active_tree = self.create_tree_from_shape('active', active)
 
2578
        basis_tree = self.create_tree_from_shape('basis', basis)
 
2579
        inv_delta = self.create_inv_delta(delta, 'target')
 
2580
        state = self.create_empty_dirstate()
 
2581
        state.set_state_from_scratch(active_tree.root_inventory,
 
2582
            [('basis', basis_tree)], [])
 
2583
        self.assertRaises(errors.InconsistentDelta,
 
2584
            state.update_basis_by_delta, inv_delta, 'target')
 
2585
        ## try:
 
2586
        ##     state.update_basis_by_delta(inv_delta, 'target')
 
2587
        ## except errors.InconsistentDelta, e:
 
2588
        ##     import pdb; pdb.set_trace()
 
2589
        ## else:
 
2590
        ##     import pdb; pdb.set_trace()
 
2591
        self.assertTrue(state._changes_aborted)
 
2592
 
 
2593
    def test_remove_file_matching_active_state(self):
 
2594
        state = self.assertUpdate(
 
2595
            active=[],
 
2596
            basis =[('file', b'file-id')],
 
2597
            target=[],
 
2598
            )
 
2599
 
 
2600
    def test_remove_file_present_in_active_state(self):
 
2601
        state = self.assertUpdate(
 
2602
            active=[('file', b'file-id')],
 
2603
            basis =[('file', b'file-id')],
 
2604
            target=[],
 
2605
            )
 
2606
 
 
2607
    def test_remove_file_present_elsewhere_in_active_state(self):
 
2608
        state = self.assertUpdate(
 
2609
            active=[('other-file', b'file-id')],
 
2610
            basis =[('file', b'file-id')],
 
2611
            target=[],
 
2612
            )
 
2613
 
 
2614
    def test_remove_file_active_state_has_diff_file(self):
 
2615
        state = self.assertUpdate(
 
2616
            active=[('file', b'file-id-2')],
 
2617
            basis =[('file', b'file-id')],
 
2618
            target=[],
 
2619
            )
 
2620
 
 
2621
    def test_remove_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2622
        state = self.assertUpdate(
 
2623
            active=[('file', b'file-id-2'),
 
2624
                    ('other-file', b'file-id')],
 
2625
            basis =[('file', b'file-id')],
 
2626
            target=[],
 
2627
            )
 
2628
 
 
2629
    def test_add_file_matching_active_state(self):
 
2630
        state = self.assertUpdate(
 
2631
            active=[('file', b'file-id')],
 
2632
            basis =[],
 
2633
            target=[('file', b'file-id')],
 
2634
            )
 
2635
 
 
2636
    def test_add_file_in_empty_dir_not_matching_active_state(self):
 
2637
        state = self.assertUpdate(
 
2638
                active=[],
 
2639
                basis=[('dir/', 'dir-id')],
 
2640
                target=[('dir/', 'dir-id', 'basis'), ('dir/file', b'file-id')],
 
2641
                )
 
2642
 
 
2643
    def test_add_file_missing_in_active_state(self):
 
2644
        state = self.assertUpdate(
 
2645
            active=[],
 
2646
            basis =[],
 
2647
            target=[('file', b'file-id')],
 
2648
            )
 
2649
 
 
2650
    def test_add_file_elsewhere_in_active_state(self):
 
2651
        state = self.assertUpdate(
 
2652
            active=[('other-file', b'file-id')],
 
2653
            basis =[],
 
2654
            target=[('file', b'file-id')],
 
2655
            )
 
2656
 
 
2657
    def test_add_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2658
        state = self.assertUpdate(
 
2659
            active=[('other-file', b'file-id'),
 
2660
                    ('file', b'file-id-2')],
 
2661
            basis =[],
 
2662
            target=[('file', b'file-id')],
 
2663
            )
 
2664
 
 
2665
    def test_rename_file_matching_active_state(self):
 
2666
        state = self.assertUpdate(
 
2667
            active=[('other-file', b'file-id')],
 
2668
            basis =[('file', b'file-id')],
 
2669
            target=[('other-file', b'file-id')],
 
2670
            )
 
2671
 
 
2672
    def test_rename_file_missing_in_active_state(self):
 
2673
        state = self.assertUpdate(
 
2674
            active=[],
 
2675
            basis =[('file', b'file-id')],
 
2676
            target=[('other-file', b'file-id')],
 
2677
            )
 
2678
 
 
2679
    def test_rename_file_present_elsewhere_in_active_state(self):
 
2680
        state = self.assertUpdate(
 
2681
            active=[('third', b'file-id')],
 
2682
            basis =[('file', b'file-id')],
 
2683
            target=[('other-file', b'file-id')],
 
2684
            )
 
2685
 
 
2686
    def test_rename_file_active_state_has_diff_source_file(self):
 
2687
        state = self.assertUpdate(
 
2688
            active=[('file', b'file-id-2')],
 
2689
            basis =[('file', b'file-id')],
 
2690
            target=[('other-file', b'file-id')],
 
2691
            )
 
2692
 
 
2693
    def test_rename_file_active_state_has_diff_target_file(self):
 
2694
        state = self.assertUpdate(
 
2695
            active=[('other-file', b'file-id-2')],
 
2696
            basis =[('file', b'file-id')],
 
2697
            target=[('other-file', b'file-id')],
 
2698
            )
 
2699
 
 
2700
    def test_rename_file_active_has_swapped_files(self):
 
2701
        state = self.assertUpdate(
 
2702
            active=[('file', b'file-id'),
 
2703
                    ('other-file', b'file-id-2')],
 
2704
            basis= [('file', b'file-id'),
 
2705
                    ('other-file', b'file-id-2')],
 
2706
            target=[('file', b'file-id-2'),
 
2707
                    ('other-file', b'file-id')])
 
2708
 
 
2709
    def test_rename_file_basis_has_swapped_files(self):
 
2710
        state = self.assertUpdate(
 
2711
            active=[('file', b'file-id'),
 
2712
                    ('other-file', b'file-id-2')],
 
2713
            basis= [('file', b'file-id-2'),
 
2714
                    ('other-file', b'file-id')],
 
2715
            target=[('file', b'file-id'),
 
2716
                    ('other-file', b'file-id-2')])
 
2717
 
 
2718
    def test_rename_directory_with_contents(self):
 
2719
        state = self.assertUpdate( # active matches basis
 
2720
            active=[('dir1/', 'dir-id'),
 
2721
                    ('dir1/file', b'file-id')],
 
2722
            basis= [('dir1/', 'dir-id'),
 
2723
                    ('dir1/file', b'file-id')],
 
2724
            target=[('dir2/', 'dir-id'),
 
2725
                    ('dir2/file', b'file-id')])
 
2726
        state = self.assertUpdate( # active matches target
 
2727
            active=[('dir2/', 'dir-id'),
 
2728
                    ('dir2/file', b'file-id')],
 
2729
            basis= [('dir1/', 'dir-id'),
 
2730
                    ('dir1/file', b'file-id')],
 
2731
            target=[('dir2/', 'dir-id'),
 
2732
                    ('dir2/file', b'file-id')])
 
2733
        state = self.assertUpdate( # active empty
 
2734
            active=[],
 
2735
            basis= [('dir1/', 'dir-id'),
 
2736
                    ('dir1/file', b'file-id')],
 
2737
            target=[('dir2/', 'dir-id'),
 
2738
                    ('dir2/file', b'file-id')])
 
2739
        state = self.assertUpdate( # active present at other location
 
2740
            active=[('dir3/', 'dir-id'),
 
2741
                    ('dir3/file', b'file-id')],
 
2742
            basis= [('dir1/', 'dir-id'),
 
2743
                    ('dir1/file', b'file-id')],
 
2744
            target=[('dir2/', 'dir-id'),
 
2745
                    ('dir2/file', b'file-id')])
 
2746
        state = self.assertUpdate( # active has different ids
 
2747
            active=[('dir1/', 'dir1-id'),
 
2748
                    ('dir1/file', 'file1-id'),
 
2749
                    ('dir2/', 'dir2-id'),
 
2750
                    ('dir2/file', 'file2-id')],
 
2751
            basis= [('dir1/', 'dir-id'),
 
2752
                    ('dir1/file', b'file-id')],
 
2753
            target=[('dir2/', 'dir-id'),
 
2754
                    ('dir2/file', b'file-id')])
 
2755
 
 
2756
    def test_invalid_file_not_present(self):
 
2757
        state = self.assertBadDelta(
 
2758
            active=[('file', b'file-id')],
 
2759
            basis= [('file', b'file-id')],
 
2760
            delta=[('other-file', 'file', b'file-id')])
 
2761
 
 
2762
    def test_invalid_new_id_same_path(self):
 
2763
        # The bad entry comes after
 
2764
        state = self.assertBadDelta(
 
2765
            active=[('file', b'file-id')],
 
2766
            basis= [('file', b'file-id')],
 
2767
            delta=[(None, 'file', b'file-id-2')])
 
2768
        # The bad entry comes first
 
2769
        state = self.assertBadDelta(
 
2770
            active=[('file', b'file-id-2')],
 
2771
            basis=[('file', b'file-id-2')],
 
2772
            delta=[(None, 'file', b'file-id')])
 
2773
 
 
2774
    def test_invalid_existing_id(self):
 
2775
        state = self.assertBadDelta(
 
2776
            active=[('file', b'file-id')],
 
2777
            basis= [('file', b'file-id')],
 
2778
            delta=[(None, 'file', b'file-id')])
 
2779
 
 
2780
    def test_invalid_parent_missing(self):
 
2781
        state = self.assertBadDelta(
 
2782
            active=[],
 
2783
            basis= [],
 
2784
            delta=[(None, 'path/path2', b'file-id')])
 
2785
        # Note: we force the active tree to have the directory, by knowing how
 
2786
        #       path_to_ie handles entries with missing parents
 
2787
        state = self.assertBadDelta(
 
2788
            active=[('path/', 'path-id')],
 
2789
            basis= [],
 
2790
            delta=[(None, 'path/path2', b'file-id')])
 
2791
        state = self.assertBadDelta(
 
2792
            active=[('path/', 'path-id'),
 
2793
                    ('path/path2', b'file-id')],
 
2794
            basis= [],
 
2795
            delta=[(None, 'path/path2', b'file-id')])
 
2796
 
 
2797
    def test_renamed_dir_same_path(self):
 
2798
        # We replace the parent directory, with another parent dir. But the C
 
2799
        # file doesn't look like it has been moved.
 
2800
        state = self.assertUpdate(# Same as basis
 
2801
            active=[('dir/', 'A-id'),
 
2802
                    ('dir/B', 'B-id')],
 
2803
            basis= [('dir/', 'A-id'),
 
2804
                    ('dir/B', 'B-id')],
 
2805
            target=[('dir/', 'C-id'),
 
2806
                    ('dir/B', 'B-id')])
 
2807
        state = self.assertUpdate(# Same as target
 
2808
            active=[('dir/', 'C-id'),
 
2809
                    ('dir/B', 'B-id')],
 
2810
            basis= [('dir/', 'A-id'),
 
2811
                    ('dir/B', 'B-id')],
 
2812
            target=[('dir/', 'C-id'),
 
2813
                    ('dir/B', 'B-id')])
 
2814
        state = self.assertUpdate(# empty active
 
2815
            active=[],
 
2816
            basis= [('dir/', 'A-id'),
 
2817
                    ('dir/B', 'B-id')],
 
2818
            target=[('dir/', 'C-id'),
 
2819
                    ('dir/B', 'B-id')])
 
2820
        state = self.assertUpdate(# different active
 
2821
            active=[('dir/', 'D-id'),
 
2822
                    ('dir/B', 'B-id')],
 
2823
            basis= [('dir/', 'A-id'),
 
2824
                    ('dir/B', 'B-id')],
 
2825
            target=[('dir/', 'C-id'),
 
2826
                    ('dir/B', 'B-id')])
 
2827
 
 
2828
    def test_parent_child_swap(self):
 
2829
        state = self.assertUpdate(# Same as basis
 
2830
            active=[('A/', 'A-id'),
 
2831
                    ('A/B/', 'B-id'),
 
2832
                    ('A/B/C', 'C-id')],
 
2833
            basis= [('A/', 'A-id'),
 
2834
                    ('A/B/', 'B-id'),
 
2835
                    ('A/B/C', 'C-id')],
 
2836
            target=[('A/', 'B-id'),
 
2837
                    ('A/B/', 'A-id'),
 
2838
                    ('A/B/C', 'C-id')])
 
2839
        state = self.assertUpdate(# Same as target
 
2840
            active=[('A/', 'B-id'),
 
2841
                    ('A/B/', 'A-id'),
 
2842
                    ('A/B/C', 'C-id')],
 
2843
            basis= [('A/', 'A-id'),
 
2844
                    ('A/B/', 'B-id'),
 
2845
                    ('A/B/C', 'C-id')],
 
2846
            target=[('A/', 'B-id'),
 
2847
                    ('A/B/', 'A-id'),
 
2848
                    ('A/B/C', 'C-id')])
 
2849
        state = self.assertUpdate(# empty active
 
2850
            active=[],
 
2851
            basis= [('A/', 'A-id'),
 
2852
                    ('A/B/', 'B-id'),
 
2853
                    ('A/B/C', 'C-id')],
 
2854
            target=[('A/', 'B-id'),
 
2855
                    ('A/B/', 'A-id'),
 
2856
                    ('A/B/C', 'C-id')])
 
2857
        state = self.assertUpdate(# different active
 
2858
            active=[('D/', 'A-id'),
 
2859
                    ('D/E/', 'B-id'),
 
2860
                    ('F', 'C-id')],
 
2861
            basis= [('A/', 'A-id'),
 
2862
                    ('A/B/', 'B-id'),
 
2863
                    ('A/B/C', 'C-id')],
 
2864
            target=[('A/', 'B-id'),
 
2865
                    ('A/B/', 'A-id'),
 
2866
                    ('A/B/C', 'C-id')])
 
2867
 
 
2868
    def test_change_root_id(self):
 
2869
        state = self.assertUpdate( # same as basis
 
2870
            active=[('', 'root-id'),
 
2871
                    ('file', b'file-id')],
 
2872
            basis= [('', 'root-id'),
 
2873
                    ('file', b'file-id')],
 
2874
            target=[('', 'target-root-id'),
 
2875
                    ('file', b'file-id')])
 
2876
        state = self.assertUpdate( # same as target
 
2877
            active=[('', 'target-root-id'),
 
2878
                    ('file', b'file-id')],
 
2879
            basis= [('', 'root-id'),
 
2880
                    ('file', b'file-id')],
 
2881
            target=[('', 'target-root-id'),
 
2882
                    ('file', 'root-id')])
 
2883
        state = self.assertUpdate( # all different
 
2884
            active=[('', 'active-root-id'),
 
2885
                    ('file', b'file-id')],
 
2886
            basis= [('', 'root-id'),
 
2887
                    ('file', b'file-id')],
 
2888
            target=[('', 'target-root-id'),
 
2889
                    ('file', 'root-id')])
 
2890
 
 
2891
    def test_change_file_absent_in_active(self):
 
2892
        state = self.assertUpdate(
 
2893
            active=[],
 
2894
            basis= [('file', b'file-id')],
 
2895
            target=[('file', b'file-id')])
 
2896
 
 
2897
    def test_invalid_changed_file(self):
 
2898
        state = self.assertBadDelta( # Not present in basis
 
2899
            active=[('file', b'file-id')],
 
2900
            basis= [],
 
2901
            delta=[('file', 'file', b'file-id')])
 
2902
        state = self.assertBadDelta( # present at another location in basis
 
2903
            active=[('file', b'file-id')],
 
2904
            basis= [('other-file', b'file-id')],
 
2905
            delta=[('file', 'file', b'file-id')])