~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: John Arbash Meinel
  • Date: 2011-05-26 08:05:45 UTC
  • mfrom: (5916 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5917.
  • Revision ID: john@arbash-meinel.com-20110526080545-5tprxfczyj4bfk0o
Merge bzr.dev 5916 and make sure the right patch is applied.

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
 
1346
1351
        stat = os.lstat(path)
1347
1352
        return dirstate.update_entry(state, entry, os.path.abspath(path), stat)
1348
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
 
1349
1367
    def test_worth_saving_limit_avoids_writing(self):
1350
1368
        tree = self.make_branch_and_tree('.')
1351
1369
        self.build_tree(['c', 'd'])
1361
1379
        state.adjust_time(+20) # Allow things to be cached
1362
1380
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1363
1381
                         state._dirblock_state)
1364
 
        f = open(state._filename, 'rb')
1365
 
        try:
1366
 
            content = f.read()
1367
 
        finally:
1368
 
            f.close()
 
1382
        content = self._read_state_content(state)
1369
1383
        self.do_update_entry(state, 'c')
1370
1384
        self.assertEqual(1, len(state._known_hash_changes))
1371
1385
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
1375
1389
        # hash values haven't been written out.
1376
1390
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
1377
1391
                         state._dirblock_state)
1378
 
        self.assertFileEqual(content, state._filename)
 
1392
        self.assertEqual(content, self._read_state_content(state))
1379
1393
        self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
1380
1394
                         state._dirblock_state)
1381
1395
        self.do_update_entry(state, 'd')
2421
2435
        self.assertTrue(len(statvalue) >= 10)
2422
2436
        self.assertEqual(len(text), statvalue.st_size)
2423
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')])