~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: Vincent Ladeuil
  • Date: 2012-02-14 17:22:37 UTC
  • mfrom: (6466 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120214172237-7dv7er3n4uy8d5m4
Merge 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,
29
 
    )
30
 
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
    )
31
39
from bzrlib.tests.scenarios import load_tests_apply_scenarios
32
40
 
33
41
 
527
535
 
528
536
class TestDirStateOnFile(TestCaseWithDirState):
529
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
 
530
551
    def test_construct_with_path(self):
531
552
        tree = self.make_branch_and_tree('tree')
532
553
        state = dirstate.DirState.from_tree(tree, 'dirstate.from_tree')
561
582
            state.unlock()
562
583
 
563
584
    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()
 
585
        state = self.create_updated_dirstate()
576
586
        try:
577
587
            entry = state._get_entry(0, path_utf8='a-file')
578
588
            # The current size should be 0 (default)
579
589
            self.assertEqual(0, entry[1][0][2])
580
590
            # We should have a real entry.
581
591
            self.assertNotEqual((None, None), entry)
582
 
            # Make sure everything is old enough
 
592
            # Set the cutoff-time into the future, so things look cacheable
583
593
            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)
 
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)
591
600
 
592
601
            # The dirblock has been updated
593
 
            self.assertEqual(7, entry[1][0][2])
594
 
            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,
595
604
                             state._dirblock_state)
596
605
 
597
606
            del entry
606
615
        state.lock_read()
607
616
        try:
608
617
            entry = state._get_entry(0, path_utf8='a-file')
609
 
            self.assertEqual(7, entry[1][0][2])
 
618
            self.assertEqual(st.st_size, entry[1][0][2])
610
619
        finally:
611
620
            state.unlock()
612
621
 
613
622
    def test_save_fails_quietly_if_locked(self):
614
623
        """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()
 
624
        state = self.create_updated_dirstate()
626
625
        try:
627
626
            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,
 
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,
633
637
                             state._dirblock_state)
634
638
 
635
639
            # Now, before we try to save, grab another dirstate, and take out a
752
756
    def test_set_state_from_inventory_no_content_no_parents(self):
753
757
        # setting the current inventory is a slow but important api to support.
754
758
        tree1, revid1 = self.make_minimal_tree()
755
 
        inv = tree1.inventory
 
759
        inv = tree1.root_inventory
756
760
        root_id = inv.path2id('')
757
761
        expected_result = [], [
758
762
            (('', '', root_id), [
773
777
 
774
778
    def test_set_state_from_scratch_no_parents(self):
775
779
        tree1, revid1 = self.make_minimal_tree()
776
 
        inv = tree1.inventory
 
780
        inv = tree1.root_inventory
777
781
        root_id = inv.path2id('')
778
782
        expected_result = [], [
779
783
            (('', '', root_id), [
794
798
 
795
799
    def test_set_state_from_scratch_identical_parent(self):
796
800
        tree1, revid1 = self.make_minimal_tree()
797
 
        inv = tree1.inventory
 
801
        inv = tree1.root_inventory
798
802
        root_id = inv.path2id('')
799
803
        rev_tree1 = tree1.branch.repository.revision_tree(revid1)
800
804
        d_entry = ('d', '', 0, False, dirstate.DirState.NULLSTAT)
852
856
                tree._dirstate._get_entry(0, 'foo-id'))
853
857
 
854
858
            # extract the inventory, and add something to it
855
 
            inv = tree._get_inventory()
 
859
            inv = tree._get_root_inventory()
856
860
            # should see the file we poked in...
857
861
            self.assertTrue(inv.has_id('foo-id'))
858
862
            self.assertTrue(inv.has_filename('foo'))
888
892
                      ['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
889
893
            tree1.commit('rev1', rev_id='rev1')
890
894
            root_id = tree1.get_root_id()
891
 
            inv = tree1.inventory
 
895
            inv = tree1.root_inventory
892
896
        finally:
893
897
            tree1.unlock()
894
898
        expected_result1 = [('', '', root_id, 'd'),
1208
1212
        # The most trivial addition of a symlink when there are no parents and
1209
1213
        # its in the root and all data about the file is supplied
1210
1214
        # bzr doesn't support fake symlinks on windows, yet.
1211
 
        self.requireFeature(tests.SymlinkFeature)
 
1215
        self.requireFeature(features.SymlinkFeature)
1212
1216
        os.symlink(target, link_name)
1213
1217
        stat = os.lstat(link_name)
1214
1218
        expected_entries = [
1239
1243
        self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
1240
1244
 
1241
1245
    def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1242
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
1246
        self.requireFeature(features.UnicodeFilenameFeature)
1243
1247
        self._test_add_symlink_to_root_no_parents_all_data(
1244
1248
            u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1245
1249
 
1322
1326
        try:
1323
1327
            tree1.add(['b'], ['b-id'])
1324
1328
            root_id = tree1.get_root_id()
1325
 
            inv = tree1.inventory
 
1329
            inv = tree1.root_inventory
1326
1330
            state = dirstate.DirState.initialize('dirstate')
1327
1331
            try:
1328
1332
                # Set the initial state with 'b'
1343
1347
            tree1.unlock()
1344
1348
 
1345
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
 
1346
1406
class TestGetLines(TestCaseWithDirState):
1347
1407
 
1348
1408
    def test_get_line_with_2_rows(self):
1741
1801
class InstrumentedDirState(dirstate.DirState):
1742
1802
    """An DirState with instrumented sha1 functionality."""
1743
1803
 
1744
 
    def __init__(self, path, sha1_provider):
1745
 
        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)
1746
1807
        self._time_offset = 0
1747
1808
        self._log = []
1748
1809
        # member is dynamically set in DirState.__init__ to turn on trace
2149
2210
class TestDirstateTreeReference(TestCaseWithDirState):
2150
2211
 
2151
2212
    def test_reference_revision_is_none(self):
2152
 
        tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
 
2213
        tree = self.make_branch_and_tree('tree', format='development-subtree')
2153
2214
        subtree = self.make_branch_and_tree('tree/subtree',
2154
 
                            format='dirstate-with-subtree')
 
2215
                            format='development-subtree')
2155
2216
        subtree.set_root_id('subtree')
2156
2217
        tree.add_reference(subtree)
2157
2218
        tree.add('subtree')
2377
2438
        self.assertTrue(len(statvalue) >= 10)
2378
2439
        self.assertEqual(len(text), statvalue.st_size)
2379
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')])