/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: Ross Lagerwall
  • Date: 2012-02-24 20:25:48 UTC
  • mto: This revision was merged to the branch mainline in revision 6480.
  • Revision ID: rosslagerwall@gmail.com-20120224202548-yvz0hy5hv5i4ittc
Update docs & contrib to use new --listen & --port options to bzr serve.

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
22
from bzrlib import (
 
23
    bzrdir,
23
24
    dirstate,
24
25
    errors,
25
26
    inventory,
26
27
    memorytree,
27
28
    osutils,
28
29
    revision as _mod_revision,
 
30
    revisiontree,
29
31
    tests,
30
 
    )
31
 
from bzrlib.tests import test_osutils
 
32
    workingtree_4,
 
33
    )
 
34
from bzrlib.transport import memory
 
35
from bzrlib.tests import (
 
36
    features,
 
37
    test_osutils,
 
38
    )
 
39
from bzrlib.tests.scenarios import load_tests_apply_scenarios
32
40
 
33
41
 
34
42
# TODO:
44
52
# set_path_id  setting id when state is in memory modified
45
53
 
46
54
 
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
 
55
load_tests = load_tests_apply_scenarios
55
56
 
56
57
 
57
58
class TestCaseWithDirState(tests.TestCaseWithTransport):
58
59
    """Helper functions for creating DirState objects with various content."""
59
60
 
 
61
    scenarios = test_osutils.dir_reader_scenarios()
 
62
 
60
63
    # Set by load_tests
61
64
    _dir_reader_class = None
62
65
    _native_to_unicode = None # Not used yet
532
535
 
533
536
class TestDirStateOnFile(TestCaseWithDirState):
534
537
 
 
538
    def create_updated_dirstate(self):
 
539
        self.build_tree(['a-file'])
 
540
        tree = self.make_branch_and_tree('.')
 
541
        tree.add(['a-file'], ['a-id'])
 
542
        tree.commit('add a-file')
 
543
        # Save and unlock the state, re-open it in readonly mode
 
544
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
545
        state.save()
 
546
        state.unlock()
 
547
        state = dirstate.DirState.on_file('dirstate')
 
548
        state.lock_read()
 
549
        return state
 
550
 
535
551
    def test_construct_with_path(self):
536
552
        tree = self.make_branch_and_tree('tree')
537
553
        state = dirstate.DirState.from_tree(tree, 'dirstate.from_tree')
566
582
            state.unlock()
567
583
 
568
584
    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()
 
585
        state = self.create_updated_dirstate()
581
586
        try:
582
587
            entry = state._get_entry(0, path_utf8='a-file')
583
588
            # The current size should be 0 (default)
584
589
            self.assertEqual(0, entry[1][0][2])
585
590
            # We should have a real entry.
586
591
            self.assertNotEqual((None, None), entry)
587
 
            # Make sure everything is old enough
 
592
            # Set the cutoff-time into the future, so things look cacheable
588
593
            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)
 
594
            state._cutoff_time += 10.0
 
595
            st = os.lstat('a-file')
 
596
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
597
            # We updated the current sha1sum because the file is cacheable
 
598
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
599
                             sha1sum)
596
600
 
597
601
            # The dirblock has been updated
598
 
            self.assertEqual(7, entry[1][0][2])
599
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
602
            self.assertEqual(st.st_size, entry[1][0][2])
 
603
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
600
604
                             state._dirblock_state)
601
605
 
602
606
            del entry
611
615
        state.lock_read()
612
616
        try:
613
617
            entry = state._get_entry(0, path_utf8='a-file')
614
 
            self.assertEqual(7, entry[1][0][2])
 
618
            self.assertEqual(st.st_size, entry[1][0][2])
615
619
        finally:
616
620
            state.unlock()
617
621
 
618
622
    def test_save_fails_quietly_if_locked(self):
619
623
        """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()
 
624
        state = self.create_updated_dirstate()
631
625
        try:
632
626
            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,
 
627
            # No cached sha1 yet.
 
628
            self.assertEqual('', entry[1][0][1])
 
629
            # Set the cutoff-time into the future, so things look cacheable
 
630
            state._sha_cutoff_time()
 
631
            state._cutoff_time += 10.0
 
632
            st = os.lstat('a-file')
 
633
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
634
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
635
                             sha1sum)
 
636
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
638
637
                             state._dirblock_state)
639
638
 
640
639
            # Now, before we try to save, grab another dirstate, and take out a
730
729
 
731
730
class TestDirStateManipulations(TestCaseWithDirState):
732
731
 
 
732
    def make_minimal_tree(self):
 
733
        tree1 = self.make_branch_and_memory_tree('tree1')
 
734
        tree1.lock_write()
 
735
        self.addCleanup(tree1.unlock)
 
736
        tree1.add('')
 
737
        revid1 = tree1.commit('foo')
 
738
        return tree1, revid1
 
739
 
 
740
    def test_update_minimal_updates_id_index(self):
 
741
        state = self.create_dirstate_with_root_and_subdir()
 
742
        self.addCleanup(state.unlock)
 
743
        id_index = state._get_id_index()
 
744
        self.assertEqual(['a-root-value', 'subdir-id'], sorted(id_index))
 
745
        state.add('file-name', 'file-id', 'file', None, '')
 
746
        self.assertEqual(['a-root-value', 'file-id', 'subdir-id'],
 
747
                         sorted(id_index))
 
748
        state.update_minimal(('', 'new-name', 'file-id'), 'f',
 
749
                             path_utf8='new-name')
 
750
        self.assertEqual(['a-root-value', 'file-id', 'subdir-id'],
 
751
                         sorted(id_index))
 
752
        self.assertEqual([('', 'new-name', 'file-id')],
 
753
                         sorted(id_index['file-id']))
 
754
        state._validate()
 
755
 
733
756
    def test_set_state_from_inventory_no_content_no_parents(self):
734
757
        # 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()
 
758
        tree1, revid1 = self.make_minimal_tree()
 
759
        inv = tree1.root_inventory
 
760
        root_id = inv.path2id('')
744
761
        expected_result = [], [
745
762
            (('', '', root_id), [
746
763
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
758
775
            # This will unlock it
759
776
            self.check_state_with_reopen(expected_result, state)
760
777
 
 
778
    def test_set_state_from_scratch_no_parents(self):
 
779
        tree1, revid1 = self.make_minimal_tree()
 
780
        inv = tree1.root_inventory
 
781
        root_id = inv.path2id('')
 
782
        expected_result = [], [
 
783
            (('', '', root_id), [
 
784
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
 
785
        state = dirstate.DirState.initialize('dirstate')
 
786
        try:
 
787
            state.set_state_from_scratch(inv, [], [])
 
788
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
789
                             state._header_state)
 
790
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
791
                             state._dirblock_state)
 
792
        except:
 
793
            state.unlock()
 
794
            raise
 
795
        else:
 
796
            # This will unlock it
 
797
            self.check_state_with_reopen(expected_result, state)
 
798
 
 
799
    def test_set_state_from_scratch_identical_parent(self):
 
800
        tree1, revid1 = self.make_minimal_tree()
 
801
        inv = tree1.root_inventory
 
802
        root_id = inv.path2id('')
 
803
        rev_tree1 = tree1.branch.repository.revision_tree(revid1)
 
804
        d_entry = ('d', '', 0, False, dirstate.DirState.NULLSTAT)
 
805
        parent_entry = ('d', '', 0, False, revid1)
 
806
        expected_result = [revid1], [
 
807
            (('', '', root_id), [d_entry, parent_entry])]
 
808
        state = dirstate.DirState.initialize('dirstate')
 
809
        try:
 
810
            state.set_state_from_scratch(inv, [(revid1, rev_tree1)], [])
 
811
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
812
                             state._header_state)
 
813
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
814
                             state._dirblock_state)
 
815
        except:
 
816
            state.unlock()
 
817
            raise
 
818
        else:
 
819
            # This will unlock it
 
820
            self.check_state_with_reopen(expected_result, state)
 
821
 
761
822
    def test_set_state_from_inventory_preserves_hashcache(self):
762
823
        # https://bugs.launchpad.net/bzr/+bug/146176
763
824
        # set_state_from_inventory should preserve the stat and hash value for
795
856
                tree._dirstate._get_entry(0, 'foo-id'))
796
857
 
797
858
            # extract the inventory, and add something to it
798
 
            inv = tree._get_inventory()
 
859
            inv = tree._get_root_inventory()
799
860
            # should see the file we poked in...
800
861
            self.assertTrue(inv.has_id('foo-id'))
801
862
            self.assertTrue(inv.has_filename('foo'))
831
892
                      ['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
832
893
            tree1.commit('rev1', rev_id='rev1')
833
894
            root_id = tree1.get_root_id()
834
 
            inv = tree1.inventory
 
895
            inv = tree1.root_inventory
835
896
        finally:
836
897
            tree1.unlock()
837
898
        expected_result1 = [('', '', root_id, 'd'),
1151
1212
        # The most trivial addition of a symlink when there are no parents and
1152
1213
        # its in the root and all data about the file is supplied
1153
1214
        # bzr doesn't support fake symlinks on windows, yet.
1154
 
        self.requireFeature(tests.SymlinkFeature)
 
1215
        self.requireFeature(features.SymlinkFeature)
1155
1216
        os.symlink(target, link_name)
1156
1217
        stat = os.lstat(link_name)
1157
1218
        expected_entries = [
1182
1243
        self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
1183
1244
 
1184
1245
    def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1185
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
1246
        self.requireFeature(features.UnicodeFilenameFeature)
1186
1247
        self._test_add_symlink_to_root_no_parents_all_data(
1187
1248
            u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1188
1249
 
1265
1326
        try:
1266
1327
            tree1.add(['b'], ['b-id'])
1267
1328
            root_id = tree1.get_root_id()
1268
 
            inv = tree1.inventory
 
1329
            inv = tree1.root_inventory
1269
1330
            state = dirstate.DirState.initialize('dirstate')
1270
1331
            try:
1271
1332
                # Set the initial state with 'b'
1286
1347
            tree1.unlock()
1287
1348
 
1288
1349
 
 
1350
class TestDirStateHashUpdates(TestCaseWithDirState):
 
1351
 
 
1352
    def do_update_entry(self, state, path):
 
1353
        entry = state._get_entry(0, path_utf8=path)
 
1354
        stat = os.lstat(path)
 
1355
        return dirstate.update_entry(state, entry, os.path.abspath(path), stat)
 
1356
 
 
1357
    def _read_state_content(self, state):
 
1358
        """Read the content of the dirstate file.
 
1359
 
 
1360
        On Windows when one process locks a file, you can't even open() the
 
1361
        file in another process (to read it). So we go directly to
 
1362
        state._state_file. This should always be the exact disk representation,
 
1363
        so it is reasonable to do so.
 
1364
        DirState also always seeks before reading, so it doesn't matter if we
 
1365
        bump the file pointer.
 
1366
        """
 
1367
        state._state_file.seek(0)
 
1368
        return state._state_file.read()
 
1369
 
 
1370
    def test_worth_saving_limit_avoids_writing(self):
 
1371
        tree = self.make_branch_and_tree('.')
 
1372
        self.build_tree(['c', 'd'])
 
1373
        tree.lock_write()
 
1374
        tree.add(['c', 'd'], ['c-id', 'd-id'])
 
1375
        tree.commit('add c and d')
 
1376
        state = InstrumentedDirState.on_file(tree.current_dirstate()._filename,
 
1377
                                             worth_saving_limit=2)
 
1378
        tree.unlock()
 
1379
        state.lock_write()
 
1380
        self.addCleanup(state.unlock)
 
1381
        state._read_dirblocks_if_needed()
 
1382
        state.adjust_time(+20) # Allow things to be cached
 
1383
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1384
                         state._dirblock_state)
 
1385
        content = self._read_state_content(state)
 
1386
        self.do_update_entry(state, 'c')
 
1387
        self.assertEqual(1, len(state._known_hash_changes))
 
1388
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1389
                         state._dirblock_state)
 
1390
        state.save()
 
1391
        # It should not have set the state to IN_MEMORY_UNMODIFIED because the
 
1392
        # hash values haven't been written out.
 
1393
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1394
                         state._dirblock_state)
 
1395
        self.assertEqual(content, self._read_state_content(state))
 
1396
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1397
                         state._dirblock_state)
 
1398
        self.do_update_entry(state, 'd')
 
1399
        self.assertEqual(2, len(state._known_hash_changes))
 
1400
        state.save()
 
1401
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1402
                         state._dirblock_state)
 
1403
        self.assertEqual(0, len(state._known_hash_changes))
 
1404
 
 
1405
 
1289
1406
class TestGetLines(TestCaseWithDirState):
1290
1407
 
1291
1408
    def test_get_line_with_2_rows(self):
1684
1801
class InstrumentedDirState(dirstate.DirState):
1685
1802
    """An DirState with instrumented sha1 functionality."""
1686
1803
 
1687
 
    def __init__(self, path, sha1_provider):
1688
 
        super(InstrumentedDirState, self).__init__(path, sha1_provider)
 
1804
    def __init__(self, path, sha1_provider, worth_saving_limit=0):
 
1805
        super(InstrumentedDirState, self).__init__(path, sha1_provider,
 
1806
            worth_saving_limit=worth_saving_limit)
1689
1807
        self._time_offset = 0
1690
1808
        self._log = []
1691
1809
        # member is dynamically set in DirState.__init__ to turn on trace
2092
2210
class TestDirstateTreeReference(TestCaseWithDirState):
2093
2211
 
2094
2212
    def test_reference_revision_is_none(self):
2095
 
        tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
 
2213
        tree = self.make_branch_and_tree('tree', format='development-subtree')
2096
2214
        subtree = self.make_branch_and_tree('tree/subtree',
2097
 
                            format='dirstate-with-subtree')
 
2215
                            format='development-subtree')
2098
2216
        subtree.set_root_id('subtree')
2099
2217
        tree.add_reference(subtree)
2100
2218
        tree.add('subtree')
2320
2438
        self.assertTrue(len(statvalue) >= 10)
2321
2439
        self.assertEqual(len(text), statvalue.st_size)
2322
2440
        self.assertEqual(expected_sha, sha1)
 
2441
 
 
2442
 
 
2443
class _Repo(object):
 
2444
    """A minimal api to get InventoryRevisionTree to work."""
 
2445
 
 
2446
    def __init__(self):
 
2447
        default_format = bzrdir.format_registry.make_bzrdir('default')
 
2448
        self._format = default_format.repository_format
 
2449
 
 
2450
    def lock_read(self):
 
2451
        pass
 
2452
 
 
2453
    def unlock(self):
 
2454
        pass
 
2455
 
 
2456
 
 
2457
class TestUpdateBasisByDelta(tests.TestCase):
 
2458
 
 
2459
    def path_to_ie(self, path, file_id, rev_id, dir_ids):
 
2460
        if path.endswith('/'):
 
2461
            is_dir = True
 
2462
            path = path[:-1]
 
2463
        else:
 
2464
            is_dir = False
 
2465
        dirname, basename = osutils.split(path)
 
2466
        try:
 
2467
            dir_id = dir_ids[dirname]
 
2468
        except KeyError:
 
2469
            dir_id = osutils.basename(dirname) + '-id'
 
2470
        if is_dir:
 
2471
            ie = inventory.InventoryDirectory(file_id, basename, dir_id)
 
2472
            dir_ids[path] = file_id
 
2473
        else:
 
2474
            ie = inventory.InventoryFile(file_id, basename, dir_id)
 
2475
            ie.text_size = 0
 
2476
            ie.text_sha1 = ''
 
2477
        ie.revision = rev_id
 
2478
        return ie
 
2479
 
 
2480
    def create_tree_from_shape(self, rev_id, shape):
 
2481
        dir_ids = {'': 'root-id'}
 
2482
        inv = inventory.Inventory('root-id', rev_id)
 
2483
        for path, file_id in shape:
 
2484
            if path == '':
 
2485
                # Replace the root entry
 
2486
                del inv._byid[inv.root.file_id]
 
2487
                inv.root.file_id = file_id
 
2488
                inv._byid[file_id] = inv.root
 
2489
                dir_ids[''] = file_id
 
2490
                continue
 
2491
            inv.add(self.path_to_ie(path, file_id, rev_id, dir_ids))
 
2492
        return revisiontree.InventoryRevisionTree(_Repo(), inv, rev_id)
 
2493
 
 
2494
    def create_empty_dirstate(self):
 
2495
        fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
 
2496
        self.addCleanup(os.remove, path)
 
2497
        os.close(fd)
 
2498
        state = dirstate.DirState.initialize(path)
 
2499
        self.addCleanup(state.unlock)
 
2500
        return state
 
2501
 
 
2502
    def create_inv_delta(self, delta, rev_id):
 
2503
        """Translate a 'delta shape' into an actual InventoryDelta"""
 
2504
        dir_ids = {'': 'root-id'}
 
2505
        inv_delta = []
 
2506
        for old_path, new_path, file_id in delta:
 
2507
            if old_path is not None and old_path.endswith('/'):
 
2508
                # Don't have to actually do anything for this, because only
 
2509
                # new_path creates InventoryEntries
 
2510
                old_path = old_path[:-1]
 
2511
            if new_path is None: # Delete
 
2512
                inv_delta.append((old_path, None, file_id, None))
 
2513
                continue
 
2514
            ie = self.path_to_ie(new_path, file_id, rev_id, dir_ids)
 
2515
            inv_delta.append((old_path, new_path, file_id, ie))
 
2516
        return inv_delta
 
2517
 
 
2518
    def assertUpdate(self, active, basis, target):
 
2519
        """Assert that update_basis_by_delta works how we want.
 
2520
 
 
2521
        Set up a DirState object with active_shape for tree 0, basis_shape for
 
2522
        tree 1. Then apply the delta from basis_shape to target_shape,
 
2523
        and assert that the DirState is still valid, and that its stored
 
2524
        content matches the target_shape.
 
2525
        """
 
2526
        active_tree = self.create_tree_from_shape('active', active)
 
2527
        basis_tree = self.create_tree_from_shape('basis', basis)
 
2528
        target_tree = self.create_tree_from_shape('target', target)
 
2529
        state = self.create_empty_dirstate()
 
2530
        state.set_state_from_scratch(active_tree.root_inventory,
 
2531
            [('basis', basis_tree)], [])
 
2532
        delta = target_tree.root_inventory._make_delta(
 
2533
            basis_tree.root_inventory)
 
2534
        state.update_basis_by_delta(delta, 'target')
 
2535
        state._validate()
 
2536
        dirstate_tree = workingtree_4.DirStateRevisionTree(state,
 
2537
            'target', _Repo())
 
2538
        # The target now that delta has been applied should match the
 
2539
        # RevisionTree
 
2540
        self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
 
2541
        # And the dirblock state should be identical to the state if we created
 
2542
        # it from scratch.
 
2543
        state2 = self.create_empty_dirstate()
 
2544
        state2.set_state_from_scratch(active_tree.root_inventory,
 
2545
            [('target', target_tree)], [])
 
2546
        self.assertEqual(state2._dirblocks, state._dirblocks)
 
2547
        return state
 
2548
 
 
2549
    def assertBadDelta(self, active, basis, delta):
 
2550
        """Test that we raise InconsistentDelta when appropriate.
 
2551
 
 
2552
        :param active: The active tree shape
 
2553
        :param basis: The basis tree shape
 
2554
        :param delta: A description of the delta to apply. Similar to the form
 
2555
            for regular inventory deltas, but omitting the InventoryEntry.
 
2556
            So adding a file is: (None, 'path', 'file-id')
 
2557
            Adding a directory is: (None, 'path/', 'dir-id')
 
2558
            Renaming a dir is: ('old/', 'new/', 'dir-id')
 
2559
            etc.
 
2560
        """
 
2561
        active_tree = self.create_tree_from_shape('active', active)
 
2562
        basis_tree = self.create_tree_from_shape('basis', basis)
 
2563
        inv_delta = self.create_inv_delta(delta, 'target')
 
2564
        state = self.create_empty_dirstate()
 
2565
        state.set_state_from_scratch(active_tree.root_inventory,
 
2566
            [('basis', basis_tree)], [])
 
2567
        self.assertRaises(errors.InconsistentDelta,
 
2568
            state.update_basis_by_delta, inv_delta, 'target')
 
2569
        ## try:
 
2570
        ##     state.update_basis_by_delta(inv_delta, 'target')
 
2571
        ## except errors.InconsistentDelta, e:
 
2572
        ##     import pdb; pdb.set_trace()
 
2573
        ## else:
 
2574
        ##     import pdb; pdb.set_trace()
 
2575
        self.assertTrue(state._changes_aborted)
 
2576
 
 
2577
    def test_remove_file_matching_active_state(self):
 
2578
        state = self.assertUpdate(
 
2579
            active=[],
 
2580
            basis =[('file', 'file-id')],
 
2581
            target=[],
 
2582
            )
 
2583
 
 
2584
    def test_remove_file_present_in_active_state(self):
 
2585
        state = self.assertUpdate(
 
2586
            active=[('file', 'file-id')],
 
2587
            basis =[('file', 'file-id')],
 
2588
            target=[],
 
2589
            )
 
2590
 
 
2591
    def test_remove_file_present_elsewhere_in_active_state(self):
 
2592
        state = self.assertUpdate(
 
2593
            active=[('other-file', 'file-id')],
 
2594
            basis =[('file', 'file-id')],
 
2595
            target=[],
 
2596
            )
 
2597
 
 
2598
    def test_remove_file_active_state_has_diff_file(self):
 
2599
        state = self.assertUpdate(
 
2600
            active=[('file', 'file-id-2')],
 
2601
            basis =[('file', 'file-id')],
 
2602
            target=[],
 
2603
            )
 
2604
 
 
2605
    def test_remove_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2606
        state = self.assertUpdate(
 
2607
            active=[('file', 'file-id-2'),
 
2608
                    ('other-file', 'file-id')],
 
2609
            basis =[('file', 'file-id')],
 
2610
            target=[],
 
2611
            )
 
2612
 
 
2613
    def test_add_file_matching_active_state(self):
 
2614
        state = self.assertUpdate(
 
2615
            active=[('file', 'file-id')],
 
2616
            basis =[],
 
2617
            target=[('file', 'file-id')],
 
2618
            )
 
2619
 
 
2620
    def test_add_file_missing_in_active_state(self):
 
2621
        state = self.assertUpdate(
 
2622
            active=[],
 
2623
            basis =[],
 
2624
            target=[('file', 'file-id')],
 
2625
            )
 
2626
 
 
2627
    def test_add_file_elsewhere_in_active_state(self):
 
2628
        state = self.assertUpdate(
 
2629
            active=[('other-file', 'file-id')],
 
2630
            basis =[],
 
2631
            target=[('file', 'file-id')],
 
2632
            )
 
2633
 
 
2634
    def test_add_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2635
        state = self.assertUpdate(
 
2636
            active=[('other-file', 'file-id'),
 
2637
                    ('file', 'file-id-2')],
 
2638
            basis =[],
 
2639
            target=[('file', 'file-id')],
 
2640
            )
 
2641
 
 
2642
    def test_rename_file_matching_active_state(self):
 
2643
        state = self.assertUpdate(
 
2644
            active=[('other-file', 'file-id')],
 
2645
            basis =[('file', 'file-id')],
 
2646
            target=[('other-file', 'file-id')],
 
2647
            )
 
2648
 
 
2649
    def test_rename_file_missing_in_active_state(self):
 
2650
        state = self.assertUpdate(
 
2651
            active=[],
 
2652
            basis =[('file', 'file-id')],
 
2653
            target=[('other-file', 'file-id')],
 
2654
            )
 
2655
 
 
2656
    def test_rename_file_present_elsewhere_in_active_state(self):
 
2657
        state = self.assertUpdate(
 
2658
            active=[('third', 'file-id')],
 
2659
            basis =[('file', 'file-id')],
 
2660
            target=[('other-file', 'file-id')],
 
2661
            )
 
2662
 
 
2663
    def test_rename_file_active_state_has_diff_source_file(self):
 
2664
        state = self.assertUpdate(
 
2665
            active=[('file', 'file-id-2')],
 
2666
            basis =[('file', 'file-id')],
 
2667
            target=[('other-file', 'file-id')],
 
2668
            )
 
2669
 
 
2670
    def test_rename_file_active_state_has_diff_target_file(self):
 
2671
        state = self.assertUpdate(
 
2672
            active=[('other-file', 'file-id-2')],
 
2673
            basis =[('file', 'file-id')],
 
2674
            target=[('other-file', 'file-id')],
 
2675
            )
 
2676
 
 
2677
    def test_rename_file_active_has_swapped_files(self):
 
2678
        state = self.assertUpdate(
 
2679
            active=[('file', 'file-id'),
 
2680
                    ('other-file', 'file-id-2')],
 
2681
            basis= [('file', 'file-id'),
 
2682
                    ('other-file', 'file-id-2')],
 
2683
            target=[('file', 'file-id-2'),
 
2684
                    ('other-file', 'file-id')])
 
2685
 
 
2686
    def test_rename_file_basis_has_swapped_files(self):
 
2687
        state = self.assertUpdate(
 
2688
            active=[('file', 'file-id'),
 
2689
                    ('other-file', 'file-id-2')],
 
2690
            basis= [('file', 'file-id-2'),
 
2691
                    ('other-file', 'file-id')],
 
2692
            target=[('file', 'file-id'),
 
2693
                    ('other-file', 'file-id-2')])
 
2694
 
 
2695
    def test_rename_directory_with_contents(self):
 
2696
        state = self.assertUpdate( # active matches basis
 
2697
            active=[('dir1/', 'dir-id'),
 
2698
                    ('dir1/file', 'file-id')],
 
2699
            basis= [('dir1/', 'dir-id'),
 
2700
                    ('dir1/file', 'file-id')],
 
2701
            target=[('dir2/', 'dir-id'),
 
2702
                    ('dir2/file', 'file-id')])
 
2703
        state = self.assertUpdate( # active matches target
 
2704
            active=[('dir2/', 'dir-id'),
 
2705
                    ('dir2/file', 'file-id')],
 
2706
            basis= [('dir1/', 'dir-id'),
 
2707
                    ('dir1/file', 'file-id')],
 
2708
            target=[('dir2/', 'dir-id'),
 
2709
                    ('dir2/file', 'file-id')])
 
2710
        state = self.assertUpdate( # active empty
 
2711
            active=[],
 
2712
            basis= [('dir1/', 'dir-id'),
 
2713
                    ('dir1/file', 'file-id')],
 
2714
            target=[('dir2/', 'dir-id'),
 
2715
                    ('dir2/file', 'file-id')])
 
2716
        state = self.assertUpdate( # active present at other location
 
2717
            active=[('dir3/', 'dir-id'),
 
2718
                    ('dir3/file', 'file-id')],
 
2719
            basis= [('dir1/', 'dir-id'),
 
2720
                    ('dir1/file', 'file-id')],
 
2721
            target=[('dir2/', 'dir-id'),
 
2722
                    ('dir2/file', 'file-id')])
 
2723
        state = self.assertUpdate( # active has different ids
 
2724
            active=[('dir1/', 'dir1-id'),
 
2725
                    ('dir1/file', 'file1-id'),
 
2726
                    ('dir2/', 'dir2-id'),
 
2727
                    ('dir2/file', 'file2-id')],
 
2728
            basis= [('dir1/', 'dir-id'),
 
2729
                    ('dir1/file', 'file-id')],
 
2730
            target=[('dir2/', 'dir-id'),
 
2731
                    ('dir2/file', 'file-id')])
 
2732
 
 
2733
    def test_invalid_file_not_present(self):
 
2734
        state = self.assertBadDelta(
 
2735
            active=[('file', 'file-id')],
 
2736
            basis= [('file', 'file-id')],
 
2737
            delta=[('other-file', 'file', 'file-id')])
 
2738
 
 
2739
    def test_invalid_new_id_same_path(self):
 
2740
        # The bad entry comes after
 
2741
        state = self.assertBadDelta(
 
2742
            active=[('file', 'file-id')],
 
2743
            basis= [('file', 'file-id')],
 
2744
            delta=[(None, 'file', 'file-id-2')])
 
2745
        # The bad entry comes first
 
2746
        state = self.assertBadDelta(
 
2747
            active=[('file', 'file-id-2')],
 
2748
            basis=[('file', 'file-id-2')],
 
2749
            delta=[(None, 'file', 'file-id')])
 
2750
 
 
2751
    def test_invalid_existing_id(self):
 
2752
        state = self.assertBadDelta(
 
2753
            active=[('file', 'file-id')],
 
2754
            basis= [('file', 'file-id')],
 
2755
            delta=[(None, 'file', 'file-id')])
 
2756
 
 
2757
    def test_invalid_parent_missing(self):
 
2758
        state = self.assertBadDelta(
 
2759
            active=[],
 
2760
            basis= [],
 
2761
            delta=[(None, 'path/path2', 'file-id')])
 
2762
        # Note: we force the active tree to have the directory, by knowing how
 
2763
        #       path_to_ie handles entries with missing parents
 
2764
        state = self.assertBadDelta(
 
2765
            active=[('path/', 'path-id')],
 
2766
            basis= [],
 
2767
            delta=[(None, 'path/path2', 'file-id')])
 
2768
        state = self.assertBadDelta(
 
2769
            active=[('path/', 'path-id'),
 
2770
                    ('path/path2', 'file-id')],
 
2771
            basis= [],
 
2772
            delta=[(None, 'path/path2', 'file-id')])
 
2773
 
 
2774
    def test_renamed_dir_same_path(self):
 
2775
        # We replace the parent directory, with another parent dir. But the C
 
2776
        # file doesn't look like it has been moved.
 
2777
        state = self.assertUpdate(# Same as basis
 
2778
            active=[('dir/', 'A-id'),
 
2779
                    ('dir/B', 'B-id')],
 
2780
            basis= [('dir/', 'A-id'),
 
2781
                    ('dir/B', 'B-id')],
 
2782
            target=[('dir/', 'C-id'),
 
2783
                    ('dir/B', 'B-id')])
 
2784
        state = self.assertUpdate(# Same as target
 
2785
            active=[('dir/', 'C-id'),
 
2786
                    ('dir/B', 'B-id')],
 
2787
            basis= [('dir/', 'A-id'),
 
2788
                    ('dir/B', 'B-id')],
 
2789
            target=[('dir/', 'C-id'),
 
2790
                    ('dir/B', 'B-id')])
 
2791
        state = self.assertUpdate(# empty active
 
2792
            active=[],
 
2793
            basis= [('dir/', 'A-id'),
 
2794
                    ('dir/B', 'B-id')],
 
2795
            target=[('dir/', 'C-id'),
 
2796
                    ('dir/B', 'B-id')])
 
2797
        state = self.assertUpdate(# different active
 
2798
            active=[('dir/', 'D-id'),
 
2799
                    ('dir/B', 'B-id')],
 
2800
            basis= [('dir/', 'A-id'),
 
2801
                    ('dir/B', 'B-id')],
 
2802
            target=[('dir/', 'C-id'),
 
2803
                    ('dir/B', 'B-id')])
 
2804
 
 
2805
    def test_parent_child_swap(self):
 
2806
        state = self.assertUpdate(# Same as basis
 
2807
            active=[('A/', 'A-id'),
 
2808
                    ('A/B/', 'B-id'),
 
2809
                    ('A/B/C', 'C-id')],
 
2810
            basis= [('A/', 'A-id'),
 
2811
                    ('A/B/', 'B-id'),
 
2812
                    ('A/B/C', 'C-id')],
 
2813
            target=[('A/', 'B-id'),
 
2814
                    ('A/B/', 'A-id'),
 
2815
                    ('A/B/C', 'C-id')])
 
2816
        state = self.assertUpdate(# Same as target
 
2817
            active=[('A/', 'B-id'),
 
2818
                    ('A/B/', 'A-id'),
 
2819
                    ('A/B/C', 'C-id')],
 
2820
            basis= [('A/', 'A-id'),
 
2821
                    ('A/B/', 'B-id'),
 
2822
                    ('A/B/C', 'C-id')],
 
2823
            target=[('A/', 'B-id'),
 
2824
                    ('A/B/', 'A-id'),
 
2825
                    ('A/B/C', 'C-id')])
 
2826
        state = self.assertUpdate(# empty active
 
2827
            active=[],
 
2828
            basis= [('A/', 'A-id'),
 
2829
                    ('A/B/', 'B-id'),
 
2830
                    ('A/B/C', 'C-id')],
 
2831
            target=[('A/', 'B-id'),
 
2832
                    ('A/B/', 'A-id'),
 
2833
                    ('A/B/C', 'C-id')])
 
2834
        state = self.assertUpdate(# different active
 
2835
            active=[('D/', 'A-id'),
 
2836
                    ('D/E/', 'B-id'),
 
2837
                    ('F', 'C-id')],
 
2838
            basis= [('A/', 'A-id'),
 
2839
                    ('A/B/', 'B-id'),
 
2840
                    ('A/B/C', 'C-id')],
 
2841
            target=[('A/', 'B-id'),
 
2842
                    ('A/B/', 'A-id'),
 
2843
                    ('A/B/C', 'C-id')])
 
2844
 
 
2845
    def test_change_root_id(self):
 
2846
        state = self.assertUpdate( # same as basis
 
2847
            active=[('', 'root-id'),
 
2848
                    ('file', 'file-id')],
 
2849
            basis= [('', 'root-id'),
 
2850
                    ('file', 'file-id')],
 
2851
            target=[('', 'target-root-id'),
 
2852
                    ('file', 'file-id')])
 
2853
        state = self.assertUpdate( # same as target
 
2854
            active=[('', 'target-root-id'),
 
2855
                    ('file', 'file-id')],
 
2856
            basis= [('', 'root-id'),
 
2857
                    ('file', 'file-id')],
 
2858
            target=[('', 'target-root-id'),
 
2859
                    ('file', 'root-id')])
 
2860
        state = self.assertUpdate( # all different
 
2861
            active=[('', 'active-root-id'),
 
2862
                    ('file', 'file-id')],
 
2863
            basis= [('', 'root-id'),
 
2864
                    ('file', 'file-id')],
 
2865
            target=[('', 'target-root-id'),
 
2866
                    ('file', 'root-id')])
 
2867
 
 
2868
    def test_change_file_absent_in_active(self):
 
2869
        state = self.assertUpdate(
 
2870
            active=[],
 
2871
            basis= [('file', 'file-id')],
 
2872
            target=[('file', 'file-id')])
 
2873
 
 
2874
    def test_invalid_changed_file(self):
 
2875
        state = self.assertBadDelta( # Not present in basis
 
2876
            active=[('file', 'file-id')],
 
2877
            basis= [],
 
2878
            delta=[('file', 'file', 'file-id')])
 
2879
        state = self.assertBadDelta( # present at another location in basis
 
2880
            active=[('file', 'file-id')],
 
2881
            basis= [('other-file', 'file-id')],
 
2882
            delta=[('file', 'file', 'file-id')])