~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: Vincent Ladeuil
  • Date: 2011-07-06 09:22:00 UTC
  • mfrom: (6008 +trunk)
  • mto: (6012.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6013.
  • Revision ID: v.ladeuil+lp@free.fr-20110706092200-7iai2mwzc0sqdsvf
MergingĀ inĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tests of the dirstate functionality being built for WorkingTreeFormat4."""
18
18
 
19
19
import os
 
20
import tempfile
20
21
 
21
22
from bzrlib import (
 
23
    bzrdir,
22
24
    dirstate,
23
25
    errors,
24
26
    inventory,
25
27
    memorytree,
26
28
    osutils,
27
29
    revision as _mod_revision,
 
30
    revisiontree,
28
31
    tests,
 
32
    workingtree_4,
29
33
    )
 
34
from bzrlib.transport import memory
30
35
from bzrlib.tests import test_osutils
31
36
from bzrlib.tests.scenarios import load_tests_apply_scenarios
32
37
 
527
532
 
528
533
class TestDirStateOnFile(TestCaseWithDirState):
529
534
 
 
535
    def create_updated_dirstate(self):
 
536
        self.build_tree(['a-file'])
 
537
        tree = self.make_branch_and_tree('.')
 
538
        tree.add(['a-file'], ['a-id'])
 
539
        tree.commit('add a-file')
 
540
        # Save and unlock the state, re-open it in readonly mode
 
541
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
542
        state.save()
 
543
        state.unlock()
 
544
        state = dirstate.DirState.on_file('dirstate')
 
545
        state.lock_read()
 
546
        return state
 
547
 
530
548
    def test_construct_with_path(self):
531
549
        tree = self.make_branch_and_tree('tree')
532
550
        state = dirstate.DirState.from_tree(tree, 'dirstate.from_tree')
561
579
            state.unlock()
562
580
 
563
581
    def test_can_save_in_read_lock(self):
564
 
        self.build_tree(['a-file'])
565
 
        state = dirstate.DirState.initialize('dirstate')
566
 
        try:
567
 
            # No stat and no sha1 sum.
568
 
            state.add('a-file', 'a-file-id', 'file', None, '')
569
 
            state.save()
570
 
        finally:
571
 
            state.unlock()
572
 
 
573
 
        # Now open in readonly mode
574
 
        state = dirstate.DirState.on_file('dirstate')
575
 
        state.lock_read()
 
582
        state = self.create_updated_dirstate()
576
583
        try:
577
584
            entry = state._get_entry(0, path_utf8='a-file')
578
585
            # The current size should be 0 (default)
579
586
            self.assertEqual(0, entry[1][0][2])
580
587
            # We should have a real entry.
581
588
            self.assertNotEqual((None, None), entry)
582
 
            # Make sure everything is old enough
 
589
            # Set the cutoff-time into the future, so things look cacheable
583
590
            state._sha_cutoff_time()
584
 
            state._cutoff_time += 10
585
 
            # Change the file length
586
 
            self.build_tree_contents([('a-file', 'shorter')])
587
 
            sha1sum = dirstate.update_entry(state, entry, 'a-file',
588
 
                os.lstat('a-file'))
589
 
            # new file, no cached sha:
590
 
            self.assertEqual(None, sha1sum)
 
591
            state._cutoff_time += 10.0
 
592
            st = os.lstat('a-file')
 
593
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
594
            # We updated the current sha1sum because the file is cacheable
 
595
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
596
                             sha1sum)
591
597
 
592
598
            # The dirblock has been updated
593
 
            self.assertEqual(7, entry[1][0][2])
594
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
599
            self.assertEqual(st.st_size, entry[1][0][2])
 
600
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
595
601
                             state._dirblock_state)
596
602
 
597
603
            del entry
606
612
        state.lock_read()
607
613
        try:
608
614
            entry = state._get_entry(0, path_utf8='a-file')
609
 
            self.assertEqual(7, entry[1][0][2])
 
615
            self.assertEqual(st.st_size, entry[1][0][2])
610
616
        finally:
611
617
            state.unlock()
612
618
 
613
619
    def test_save_fails_quietly_if_locked(self):
614
620
        """If dirstate is locked, save will fail without complaining."""
615
 
        self.build_tree(['a-file'])
616
 
        state = dirstate.DirState.initialize('dirstate')
617
 
        try:
618
 
            # No stat and no sha1 sum.
619
 
            state.add('a-file', 'a-file-id', 'file', None, '')
620
 
            state.save()
621
 
        finally:
622
 
            state.unlock()
623
 
 
624
 
        state = dirstate.DirState.on_file('dirstate')
625
 
        state.lock_read()
 
621
        state = self.create_updated_dirstate()
626
622
        try:
627
623
            entry = state._get_entry(0, path_utf8='a-file')
628
 
            sha1sum = dirstate.update_entry(state, entry, 'a-file',
629
 
                os.lstat('a-file'))
630
 
            # No sha - too new
631
 
            self.assertEqual(None, sha1sum)
632
 
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
624
            # No cached sha1 yet.
 
625
            self.assertEqual('', entry[1][0][1])
 
626
            # Set the cutoff-time into the future, so things look cacheable
 
627
            state._sha_cutoff_time()
 
628
            state._cutoff_time += 10.0
 
629
            st = os.lstat('a-file')
 
630
            sha1sum = dirstate.update_entry(state, entry, 'a-file', st)
 
631
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
 
632
                             sha1sum)
 
633
            self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
633
634
                             state._dirblock_state)
634
635
 
635
636
            # Now, before we try to save, grab another dirstate, and take out a
725
726
 
726
727
class TestDirStateManipulations(TestCaseWithDirState):
727
728
 
 
729
    def make_minimal_tree(self):
 
730
        tree1 = self.make_branch_and_memory_tree('tree1')
 
731
        tree1.lock_write()
 
732
        self.addCleanup(tree1.unlock)
 
733
        tree1.add('')
 
734
        revid1 = tree1.commit('foo')
 
735
        return tree1, revid1
 
736
 
728
737
    def test_update_minimal_updates_id_index(self):
729
738
        state = self.create_dirstate_with_root_and_subdir()
730
739
        self.addCleanup(state.unlock)
743
752
 
744
753
    def test_set_state_from_inventory_no_content_no_parents(self):
745
754
        # setting the current inventory is a slow but important api to support.
746
 
        tree1 = self.make_branch_and_memory_tree('tree1')
747
 
        tree1.lock_write()
748
 
        try:
749
 
            tree1.add('')
750
 
            revid1 = tree1.commit('foo').encode('utf8')
751
 
            root_id = tree1.get_root_id()
752
 
            inv = tree1.inventory
753
 
        finally:
754
 
            tree1.unlock()
 
755
        tree1, revid1 = self.make_minimal_tree()
 
756
        inv = tree1.inventory
 
757
        root_id = inv.path2id('')
755
758
        expected_result = [], [
756
759
            (('', '', root_id), [
757
760
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
769
772
            # This will unlock it
770
773
            self.check_state_with_reopen(expected_result, state)
771
774
 
 
775
    def test_set_state_from_scratch_no_parents(self):
 
776
        tree1, revid1 = self.make_minimal_tree()
 
777
        inv = tree1.inventory
 
778
        root_id = inv.path2id('')
 
779
        expected_result = [], [
 
780
            (('', '', root_id), [
 
781
             ('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
 
782
        state = dirstate.DirState.initialize('dirstate')
 
783
        try:
 
784
            state.set_state_from_scratch(inv, [], [])
 
785
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
786
                             state._header_state)
 
787
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
788
                             state._dirblock_state)
 
789
        except:
 
790
            state.unlock()
 
791
            raise
 
792
        else:
 
793
            # This will unlock it
 
794
            self.check_state_with_reopen(expected_result, state)
 
795
 
 
796
    def test_set_state_from_scratch_identical_parent(self):
 
797
        tree1, revid1 = self.make_minimal_tree()
 
798
        inv = tree1.inventory
 
799
        root_id = inv.path2id('')
 
800
        rev_tree1 = tree1.branch.repository.revision_tree(revid1)
 
801
        d_entry = ('d', '', 0, False, dirstate.DirState.NULLSTAT)
 
802
        parent_entry = ('d', '', 0, False, revid1)
 
803
        expected_result = [revid1], [
 
804
            (('', '', root_id), [d_entry, parent_entry])]
 
805
        state = dirstate.DirState.initialize('dirstate')
 
806
        try:
 
807
            state.set_state_from_scratch(inv, [(revid1, rev_tree1)], [])
 
808
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
809
                             state._header_state)
 
810
            self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
811
                             state._dirblock_state)
 
812
        except:
 
813
            state.unlock()
 
814
            raise
 
815
        else:
 
816
            # This will unlock it
 
817
            self.check_state_with_reopen(expected_result, state)
 
818
 
772
819
    def test_set_state_from_inventory_preserves_hashcache(self):
773
820
        # https://bugs.launchpad.net/bzr/+bug/146176
774
821
        # set_state_from_inventory should preserve the stat and hash value for
1297
1344
            tree1.unlock()
1298
1345
 
1299
1346
 
 
1347
class TestDirStateHashUpdates(TestCaseWithDirState):
 
1348
 
 
1349
    def do_update_entry(self, state, path):
 
1350
        entry = state._get_entry(0, path_utf8=path)
 
1351
        stat = os.lstat(path)
 
1352
        return dirstate.update_entry(state, entry, os.path.abspath(path), stat)
 
1353
 
 
1354
    def _read_state_content(self, state):
 
1355
        """Read the content of the dirstate file.
 
1356
 
 
1357
        On Windows when one process locks a file, you can't even open() the
 
1358
        file in another process (to read it). So we go directly to
 
1359
        state._state_file. This should always be the exact disk representation,
 
1360
        so it is reasonable to do so.
 
1361
        DirState also always seeks before reading, so it doesn't matter if we
 
1362
        bump the file pointer.
 
1363
        """
 
1364
        state._state_file.seek(0)
 
1365
        return state._state_file.read()
 
1366
 
 
1367
    def test_worth_saving_limit_avoids_writing(self):
 
1368
        tree = self.make_branch_and_tree('.')
 
1369
        self.build_tree(['c', 'd'])
 
1370
        tree.lock_write()
 
1371
        tree.add(['c', 'd'], ['c-id', 'd-id'])
 
1372
        tree.commit('add c and d')
 
1373
        state = InstrumentedDirState.on_file(tree.current_dirstate()._filename,
 
1374
                                             worth_saving_limit=2)
 
1375
        tree.unlock()
 
1376
        state.lock_write()
 
1377
        self.addCleanup(state.unlock)
 
1378
        state._read_dirblocks_if_needed()
 
1379
        state.adjust_time(+20) # Allow things to be cached
 
1380
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1381
                         state._dirblock_state)
 
1382
        content = self._read_state_content(state)
 
1383
        self.do_update_entry(state, 'c')
 
1384
        self.assertEqual(1, len(state._known_hash_changes))
 
1385
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1386
                         state._dirblock_state)
 
1387
        state.save()
 
1388
        # It should not have set the state to IN_MEMORY_UNMODIFIED because the
 
1389
        # hash values haven't been written out.
 
1390
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1391
                         state._dirblock_state)
 
1392
        self.assertEqual(content, self._read_state_content(state))
 
1393
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
 
1394
                         state._dirblock_state)
 
1395
        self.do_update_entry(state, 'd')
 
1396
        self.assertEqual(2, len(state._known_hash_changes))
 
1397
        state.save()
 
1398
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1399
                         state._dirblock_state)
 
1400
        self.assertEqual(0, len(state._known_hash_changes))
 
1401
 
 
1402
 
1300
1403
class TestGetLines(TestCaseWithDirState):
1301
1404
 
1302
1405
    def test_get_line_with_2_rows(self):
1695
1798
class InstrumentedDirState(dirstate.DirState):
1696
1799
    """An DirState with instrumented sha1 functionality."""
1697
1800
 
1698
 
    def __init__(self, path, sha1_provider):
1699
 
        super(InstrumentedDirState, self).__init__(path, sha1_provider)
 
1801
    def __init__(self, path, sha1_provider, worth_saving_limit=0):
 
1802
        super(InstrumentedDirState, self).__init__(path, sha1_provider,
 
1803
            worth_saving_limit=worth_saving_limit)
1700
1804
        self._time_offset = 0
1701
1805
        self._log = []
1702
1806
        # member is dynamically set in DirState.__init__ to turn on trace
2331
2435
        self.assertTrue(len(statvalue) >= 10)
2332
2436
        self.assertEqual(len(text), statvalue.st_size)
2333
2437
        self.assertEqual(expected_sha, sha1)
 
2438
 
 
2439
 
 
2440
class _Repo(object):
 
2441
    """A minimal api to get InventoryRevisionTree to work."""
 
2442
 
 
2443
    def __init__(self):
 
2444
        default_format = bzrdir.format_registry.make_bzrdir('default')
 
2445
        self._format = default_format.repository_format
 
2446
 
 
2447
    def lock_read(self):
 
2448
        pass
 
2449
 
 
2450
    def unlock(self):
 
2451
        pass
 
2452
 
 
2453
 
 
2454
class TestUpdateBasisByDelta(tests.TestCase):
 
2455
 
 
2456
    def path_to_ie(self, path, file_id, rev_id, dir_ids):
 
2457
        if path.endswith('/'):
 
2458
            is_dir = True
 
2459
            path = path[:-1]
 
2460
        else:
 
2461
            is_dir = False
 
2462
        dirname, basename = osutils.split(path)
 
2463
        try:
 
2464
            dir_id = dir_ids[dirname]
 
2465
        except KeyError:
 
2466
            dir_id = osutils.basename(dirname) + '-id'
 
2467
        if is_dir:
 
2468
            ie = inventory.InventoryDirectory(file_id, basename, dir_id)
 
2469
            dir_ids[path] = file_id
 
2470
        else:
 
2471
            ie = inventory.InventoryFile(file_id, basename, dir_id)
 
2472
            ie.text_size = 0
 
2473
            ie.text_sha1 = ''
 
2474
        ie.revision = rev_id
 
2475
        return ie
 
2476
 
 
2477
    def create_tree_from_shape(self, rev_id, shape):
 
2478
        dir_ids = {'': 'root-id'}
 
2479
        inv = inventory.Inventory('root-id', rev_id)
 
2480
        for path, file_id in shape:
 
2481
            if path == '':
 
2482
                # Replace the root entry
 
2483
                del inv._byid[inv.root.file_id]
 
2484
                inv.root.file_id = file_id
 
2485
                inv._byid[file_id] = inv.root
 
2486
                dir_ids[''] = file_id
 
2487
                continue
 
2488
            inv.add(self.path_to_ie(path, file_id, rev_id, dir_ids))
 
2489
        return revisiontree.InventoryRevisionTree(_Repo(), inv, rev_id)
 
2490
 
 
2491
    def create_empty_dirstate(self):
 
2492
        fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
 
2493
        self.addCleanup(os.remove, path)
 
2494
        os.close(fd)
 
2495
        state = dirstate.DirState.initialize(path)
 
2496
        self.addCleanup(state.unlock)
 
2497
        return state
 
2498
 
 
2499
    def create_inv_delta(self, delta, rev_id):
 
2500
        """Translate a 'delta shape' into an actual InventoryDelta"""
 
2501
        dir_ids = {'': 'root-id'}
 
2502
        inv_delta = []
 
2503
        for old_path, new_path, file_id in delta:
 
2504
            if old_path is not None and old_path.endswith('/'):
 
2505
                # Don't have to actually do anything for this, because only
 
2506
                # new_path creates InventoryEntries
 
2507
                old_path = old_path[:-1]
 
2508
            if new_path is None: # Delete
 
2509
                inv_delta.append((old_path, None, file_id, None))
 
2510
                continue
 
2511
            ie = self.path_to_ie(new_path, file_id, rev_id, dir_ids)
 
2512
            inv_delta.append((old_path, new_path, file_id, ie))
 
2513
        return inv_delta
 
2514
 
 
2515
    def assertUpdate(self, active, basis, target):
 
2516
        """Assert that update_basis_by_delta works how we want.
 
2517
 
 
2518
        Set up a DirState object with active_shape for tree 0, basis_shape for
 
2519
        tree 1. Then apply the delta from basis_shape to target_shape,
 
2520
        and assert that the DirState is still valid, and that its stored
 
2521
        content matches the target_shape.
 
2522
        """
 
2523
        active_tree = self.create_tree_from_shape('active', active)
 
2524
        basis_tree = self.create_tree_from_shape('basis', basis)
 
2525
        target_tree = self.create_tree_from_shape('target', target)
 
2526
        state = self.create_empty_dirstate()
 
2527
        state.set_state_from_scratch(active_tree.inventory,
 
2528
            [('basis', basis_tree)], [])
 
2529
        delta = target_tree.inventory._make_delta(basis_tree.inventory)
 
2530
        state.update_basis_by_delta(delta, 'target')
 
2531
        state._validate()
 
2532
        dirstate_tree = workingtree_4.DirStateRevisionTree(state,
 
2533
            'target', _Repo())
 
2534
        # The target now that delta has been applied should match the
 
2535
        # RevisionTree
 
2536
        self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
 
2537
        # And the dirblock state should be identical to the state if we created
 
2538
        # it from scratch.
 
2539
        state2 = self.create_empty_dirstate()
 
2540
        state2.set_state_from_scratch(active_tree.inventory,
 
2541
            [('target', target_tree)], [])
 
2542
        self.assertEqual(state2._dirblocks, state._dirblocks)
 
2543
        return state
 
2544
 
 
2545
    def assertBadDelta(self, active, basis, delta):
 
2546
        """Test that we raise InconsistentDelta when appropriate.
 
2547
 
 
2548
        :param active: The active tree shape
 
2549
        :param basis: The basis tree shape
 
2550
        :param delta: A description of the delta to apply. Similar to the form
 
2551
            for regular inventory deltas, but omitting the InventoryEntry.
 
2552
            So adding a file is: (None, 'path', 'file-id')
 
2553
            Adding a directory is: (None, 'path/', 'dir-id')
 
2554
            Renaming a dir is: ('old/', 'new/', 'dir-id')
 
2555
            etc.
 
2556
        """
 
2557
        active_tree = self.create_tree_from_shape('active', active)
 
2558
        basis_tree = self.create_tree_from_shape('basis', basis)
 
2559
        inv_delta = self.create_inv_delta(delta, 'target')
 
2560
        state = self.create_empty_dirstate()
 
2561
        state.set_state_from_scratch(active_tree.inventory,
 
2562
            [('basis', basis_tree)], [])
 
2563
        self.assertRaises(errors.InconsistentDelta,
 
2564
            state.update_basis_by_delta, inv_delta, 'target')
 
2565
        ## try:
 
2566
        ##     state.update_basis_by_delta(inv_delta, 'target')
 
2567
        ## except errors.InconsistentDelta, e:
 
2568
        ##     import pdb; pdb.set_trace()
 
2569
        ## else:
 
2570
        ##     import pdb; pdb.set_trace()
 
2571
        self.assertTrue(state._changes_aborted)
 
2572
 
 
2573
    def test_remove_file_matching_active_state(self):
 
2574
        state = self.assertUpdate(
 
2575
            active=[],
 
2576
            basis =[('file', 'file-id')],
 
2577
            target=[],
 
2578
            )
 
2579
 
 
2580
    def test_remove_file_present_in_active_state(self):
 
2581
        state = self.assertUpdate(
 
2582
            active=[('file', 'file-id')],
 
2583
            basis =[('file', 'file-id')],
 
2584
            target=[],
 
2585
            )
 
2586
 
 
2587
    def test_remove_file_present_elsewhere_in_active_state(self):
 
2588
        state = self.assertUpdate(
 
2589
            active=[('other-file', 'file-id')],
 
2590
            basis =[('file', 'file-id')],
 
2591
            target=[],
 
2592
            )
 
2593
 
 
2594
    def test_remove_file_active_state_has_diff_file(self):
 
2595
        state = self.assertUpdate(
 
2596
            active=[('file', 'file-id-2')],
 
2597
            basis =[('file', 'file-id')],
 
2598
            target=[],
 
2599
            )
 
2600
 
 
2601
    def test_remove_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2602
        state = self.assertUpdate(
 
2603
            active=[('file', 'file-id-2'),
 
2604
                    ('other-file', 'file-id')],
 
2605
            basis =[('file', 'file-id')],
 
2606
            target=[],
 
2607
            )
 
2608
 
 
2609
    def test_add_file_matching_active_state(self):
 
2610
        state = self.assertUpdate(
 
2611
            active=[('file', 'file-id')],
 
2612
            basis =[],
 
2613
            target=[('file', 'file-id')],
 
2614
            )
 
2615
 
 
2616
    def test_add_file_missing_in_active_state(self):
 
2617
        state = self.assertUpdate(
 
2618
            active=[],
 
2619
            basis =[],
 
2620
            target=[('file', 'file-id')],
 
2621
            )
 
2622
 
 
2623
    def test_add_file_elsewhere_in_active_state(self):
 
2624
        state = self.assertUpdate(
 
2625
            active=[('other-file', 'file-id')],
 
2626
            basis =[],
 
2627
            target=[('file', 'file-id')],
 
2628
            )
 
2629
 
 
2630
    def test_add_file_active_state_has_diff_file_and_file_elsewhere(self):
 
2631
        state = self.assertUpdate(
 
2632
            active=[('other-file', 'file-id'),
 
2633
                    ('file', 'file-id-2')],
 
2634
            basis =[],
 
2635
            target=[('file', 'file-id')],
 
2636
            )
 
2637
 
 
2638
    def test_rename_file_matching_active_state(self):
 
2639
        state = self.assertUpdate(
 
2640
            active=[('other-file', 'file-id')],
 
2641
            basis =[('file', 'file-id')],
 
2642
            target=[('other-file', 'file-id')],
 
2643
            )
 
2644
 
 
2645
    def test_rename_file_missing_in_active_state(self):
 
2646
        state = self.assertUpdate(
 
2647
            active=[],
 
2648
            basis =[('file', 'file-id')],
 
2649
            target=[('other-file', 'file-id')],
 
2650
            )
 
2651
 
 
2652
    def test_rename_file_present_elsewhere_in_active_state(self):
 
2653
        state = self.assertUpdate(
 
2654
            active=[('third', 'file-id')],
 
2655
            basis =[('file', 'file-id')],
 
2656
            target=[('other-file', 'file-id')],
 
2657
            )
 
2658
 
 
2659
    def test_rename_file_active_state_has_diff_source_file(self):
 
2660
        state = self.assertUpdate(
 
2661
            active=[('file', 'file-id-2')],
 
2662
            basis =[('file', 'file-id')],
 
2663
            target=[('other-file', 'file-id')],
 
2664
            )
 
2665
 
 
2666
    def test_rename_file_active_state_has_diff_target_file(self):
 
2667
        state = self.assertUpdate(
 
2668
            active=[('other-file', 'file-id-2')],
 
2669
            basis =[('file', 'file-id')],
 
2670
            target=[('other-file', 'file-id')],
 
2671
            )
 
2672
 
 
2673
    def test_rename_file_active_has_swapped_files(self):
 
2674
        state = self.assertUpdate(
 
2675
            active=[('file', 'file-id'),
 
2676
                    ('other-file', 'file-id-2')],
 
2677
            basis= [('file', 'file-id'),
 
2678
                    ('other-file', 'file-id-2')],
 
2679
            target=[('file', 'file-id-2'),
 
2680
                    ('other-file', 'file-id')])
 
2681
 
 
2682
    def test_rename_file_basis_has_swapped_files(self):
 
2683
        state = self.assertUpdate(
 
2684
            active=[('file', 'file-id'),
 
2685
                    ('other-file', 'file-id-2')],
 
2686
            basis= [('file', 'file-id-2'),
 
2687
                    ('other-file', 'file-id')],
 
2688
            target=[('file', 'file-id'),
 
2689
                    ('other-file', 'file-id-2')])
 
2690
 
 
2691
    def test_rename_directory_with_contents(self):
 
2692
        state = self.assertUpdate( # active matches basis
 
2693
            active=[('dir1/', 'dir-id'),
 
2694
                    ('dir1/file', 'file-id')],
 
2695
            basis= [('dir1/', 'dir-id'),
 
2696
                    ('dir1/file', 'file-id')],
 
2697
            target=[('dir2/', 'dir-id'),
 
2698
                    ('dir2/file', 'file-id')])
 
2699
        state = self.assertUpdate( # active matches target
 
2700
            active=[('dir2/', 'dir-id'),
 
2701
                    ('dir2/file', 'file-id')],
 
2702
            basis= [('dir1/', 'dir-id'),
 
2703
                    ('dir1/file', 'file-id')],
 
2704
            target=[('dir2/', 'dir-id'),
 
2705
                    ('dir2/file', 'file-id')])
 
2706
        state = self.assertUpdate( # active empty
 
2707
            active=[],
 
2708
            basis= [('dir1/', 'dir-id'),
 
2709
                    ('dir1/file', 'file-id')],
 
2710
            target=[('dir2/', 'dir-id'),
 
2711
                    ('dir2/file', 'file-id')])
 
2712
        state = self.assertUpdate( # active present at other location
 
2713
            active=[('dir3/', 'dir-id'),
 
2714
                    ('dir3/file', 'file-id')],
 
2715
            basis= [('dir1/', 'dir-id'),
 
2716
                    ('dir1/file', 'file-id')],
 
2717
            target=[('dir2/', 'dir-id'),
 
2718
                    ('dir2/file', 'file-id')])
 
2719
        state = self.assertUpdate( # active has different ids
 
2720
            active=[('dir1/', 'dir1-id'),
 
2721
                    ('dir1/file', 'file1-id'),
 
2722
                    ('dir2/', 'dir2-id'),
 
2723
                    ('dir2/file', 'file2-id')],
 
2724
            basis= [('dir1/', 'dir-id'),
 
2725
                    ('dir1/file', 'file-id')],
 
2726
            target=[('dir2/', 'dir-id'),
 
2727
                    ('dir2/file', 'file-id')])
 
2728
 
 
2729
    def test_invalid_file_not_present(self):
 
2730
        state = self.assertBadDelta(
 
2731
            active=[('file', 'file-id')],
 
2732
            basis= [('file', 'file-id')],
 
2733
            delta=[('other-file', 'file', 'file-id')])
 
2734
 
 
2735
    def test_invalid_new_id_same_path(self):
 
2736
        # The bad entry comes after
 
2737
        state = self.assertBadDelta(
 
2738
            active=[('file', 'file-id')],
 
2739
            basis= [('file', 'file-id')],
 
2740
            delta=[(None, 'file', 'file-id-2')])
 
2741
        # The bad entry comes first
 
2742
        state = self.assertBadDelta(
 
2743
            active=[('file', 'file-id-2')],
 
2744
            basis=[('file', 'file-id-2')],
 
2745
            delta=[(None, 'file', 'file-id')])
 
2746
 
 
2747
    def test_invalid_existing_id(self):
 
2748
        state = self.assertBadDelta(
 
2749
            active=[('file', 'file-id')],
 
2750
            basis= [('file', 'file-id')],
 
2751
            delta=[(None, 'file', 'file-id')])
 
2752
 
 
2753
    def test_invalid_parent_missing(self):
 
2754
        state = self.assertBadDelta(
 
2755
            active=[],
 
2756
            basis= [],
 
2757
            delta=[(None, 'path/path2', 'file-id')])
 
2758
        # Note: we force the active tree to have the directory, by knowing how
 
2759
        #       path_to_ie handles entries with missing parents
 
2760
        state = self.assertBadDelta(
 
2761
            active=[('path/', 'path-id')],
 
2762
            basis= [],
 
2763
            delta=[(None, 'path/path2', 'file-id')])
 
2764
        state = self.assertBadDelta(
 
2765
            active=[('path/', 'path-id'),
 
2766
                    ('path/path2', 'file-id')],
 
2767
            basis= [],
 
2768
            delta=[(None, 'path/path2', 'file-id')])
 
2769
 
 
2770
    def test_renamed_dir_same_path(self):
 
2771
        # We replace the parent directory, with another parent dir. But the C
 
2772
        # file doesn't look like it has been moved.
 
2773
        state = self.assertUpdate(# Same as basis
 
2774
            active=[('dir/', 'A-id'),
 
2775
                    ('dir/B', 'B-id')],
 
2776
            basis= [('dir/', 'A-id'),
 
2777
                    ('dir/B', 'B-id')],
 
2778
            target=[('dir/', 'C-id'),
 
2779
                    ('dir/B', 'B-id')])
 
2780
        state = self.assertUpdate(# Same as target
 
2781
            active=[('dir/', 'C-id'),
 
2782
                    ('dir/B', 'B-id')],
 
2783
            basis= [('dir/', 'A-id'),
 
2784
                    ('dir/B', 'B-id')],
 
2785
            target=[('dir/', 'C-id'),
 
2786
                    ('dir/B', 'B-id')])
 
2787
        state = self.assertUpdate(# empty active
 
2788
            active=[],
 
2789
            basis= [('dir/', 'A-id'),
 
2790
                    ('dir/B', 'B-id')],
 
2791
            target=[('dir/', 'C-id'),
 
2792
                    ('dir/B', 'B-id')])
 
2793
        state = self.assertUpdate(# different active
 
2794
            active=[('dir/', 'D-id'),
 
2795
                    ('dir/B', 'B-id')],
 
2796
            basis= [('dir/', 'A-id'),
 
2797
                    ('dir/B', 'B-id')],
 
2798
            target=[('dir/', 'C-id'),
 
2799
                    ('dir/B', 'B-id')])
 
2800
 
 
2801
    def test_parent_child_swap(self):
 
2802
        state = self.assertUpdate(# Same as basis
 
2803
            active=[('A/', 'A-id'),
 
2804
                    ('A/B/', 'B-id'),
 
2805
                    ('A/B/C', 'C-id')],
 
2806
            basis= [('A/', 'A-id'),
 
2807
                    ('A/B/', 'B-id'),
 
2808
                    ('A/B/C', 'C-id')],
 
2809
            target=[('A/', 'B-id'),
 
2810
                    ('A/B/', 'A-id'),
 
2811
                    ('A/B/C', 'C-id')])
 
2812
        state = self.assertUpdate(# Same as target
 
2813
            active=[('A/', 'B-id'),
 
2814
                    ('A/B/', 'A-id'),
 
2815
                    ('A/B/C', 'C-id')],
 
2816
            basis= [('A/', 'A-id'),
 
2817
                    ('A/B/', 'B-id'),
 
2818
                    ('A/B/C', 'C-id')],
 
2819
            target=[('A/', 'B-id'),
 
2820
                    ('A/B/', 'A-id'),
 
2821
                    ('A/B/C', 'C-id')])
 
2822
        state = self.assertUpdate(# empty active
 
2823
            active=[],
 
2824
            basis= [('A/', 'A-id'),
 
2825
                    ('A/B/', 'B-id'),
 
2826
                    ('A/B/C', 'C-id')],
 
2827
            target=[('A/', 'B-id'),
 
2828
                    ('A/B/', 'A-id'),
 
2829
                    ('A/B/C', 'C-id')])
 
2830
        state = self.assertUpdate(# different active
 
2831
            active=[('D/', 'A-id'),
 
2832
                    ('D/E/', 'B-id'),
 
2833
                    ('F', 'C-id')],
 
2834
            basis= [('A/', 'A-id'),
 
2835
                    ('A/B/', 'B-id'),
 
2836
                    ('A/B/C', 'C-id')],
 
2837
            target=[('A/', 'B-id'),
 
2838
                    ('A/B/', 'A-id'),
 
2839
                    ('A/B/C', 'C-id')])
 
2840
 
 
2841
    def test_change_root_id(self):
 
2842
        state = self.assertUpdate( # same as basis
 
2843
            active=[('', 'root-id'),
 
2844
                    ('file', 'file-id')],
 
2845
            basis= [('', 'root-id'),
 
2846
                    ('file', 'file-id')],
 
2847
            target=[('', 'target-root-id'),
 
2848
                    ('file', 'file-id')])
 
2849
        state = self.assertUpdate( # same as target
 
2850
            active=[('', 'target-root-id'),
 
2851
                    ('file', 'file-id')],
 
2852
            basis= [('', 'root-id'),
 
2853
                    ('file', 'file-id')],
 
2854
            target=[('', 'target-root-id'),
 
2855
                    ('file', 'root-id')])
 
2856
        state = self.assertUpdate( # all different
 
2857
            active=[('', 'active-root-id'),
 
2858
                    ('file', 'file-id')],
 
2859
            basis= [('', 'root-id'),
 
2860
                    ('file', 'file-id')],
 
2861
            target=[('', 'target-root-id'),
 
2862
                    ('file', 'root-id')])
 
2863
 
 
2864
    def test_change_file_absent_in_active(self):
 
2865
        state = self.assertUpdate(
 
2866
            active=[],
 
2867
            basis= [('file', 'file-id')],
 
2868
            target=[('file', 'file-id')])
 
2869
 
 
2870
    def test_invalid_changed_file(self):
 
2871
        state = self.assertBadDelta( # Not present in basis
 
2872
            active=[('file', 'file-id')],
 
2873
            basis= [],
 
2874
            delta=[('file', 'file', 'file-id')])
 
2875
        state = self.assertBadDelta( # present at another location in basis
 
2876
            active=[('file', 'file-id')],
 
2877
            basis= [('other-file', 'file-id')],
 
2878
            delta=[('file', 'file', 'file-id')])