1311
1218
self.assertRaises(errors.BzrError,
1312
1219
state.add, '..', 'ass-id', 'directory', None, None)
1314
def test_set_state_with_rename_b_a_bug_395556(self):
1315
# bug 395556 uncovered a bug where the dirstate ends up with a false
1316
# relocation record - in a tree with no parents there should be no
1317
# absent or relocated records. This then leads to further corruption
1318
# when a commit occurs, as the incorrect relocation gathers an
1319
# incorrect absent in tree 1, and future changes go to pot.
1320
tree1 = self.make_branch_and_tree('tree1')
1321
self.build_tree(['tree1/b'])
1324
tree1.add(['b'], ['b-id'])
1325
root_id = tree1.get_root_id()
1326
inv = tree1.inventory
1327
state = dirstate.DirState.initialize('dirstate')
1329
# Set the initial state with 'b'
1330
state.set_state_from_inventory(inv)
1331
inv.rename('b-id', root_id, 'a')
1332
# Set the new state with 'a', which currently corrupts.
1333
state.set_state_from_inventory(inv)
1334
expected_result1 = [('', '', root_id, 'd'),
1335
('', 'a', 'b-id', 'f'),
1338
for entry in state._iter_entries():
1339
values.append(entry[0] + entry[1][0][:1])
1340
self.assertEqual(expected_result1, values)
1347
class TestDirStateHashUpdates(TestCaseWithDirState):
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)
1354
def _read_state_content(self, state):
1355
"""Read the content of the dirstate file.
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.
1364
state._state_file.seek(0)
1365
return state._state_file.read()
1367
def test_worth_saving_limit_avoids_writing(self):
1368
tree = self.make_branch_and_tree('.')
1369
self.build_tree(['c', 'd'])
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)
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)
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))
1398
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1399
state._dirblock_state)
1400
self.assertEqual(0, len(state._known_hash_changes))
1403
1222
class TestGetLines(TestCaseWithDirState):
2435
2253
self.assertTrue(len(statvalue) >= 10)
2436
2254
self.assertEqual(len(text), statvalue.st_size)
2437
2255
self.assertEqual(expected_sha, sha1)
2440
class _Repo(object):
2441
"""A minimal api to get InventoryRevisionTree to work."""
2444
default_format = bzrdir.format_registry.make_bzrdir('default')
2445
self._format = default_format.repository_format
2447
def lock_read(self):
2454
class TestUpdateBasisByDelta(tests.TestCase):
2456
def path_to_ie(self, path, file_id, rev_id, dir_ids):
2457
if path.endswith('/'):
2462
dirname, basename = osutils.split(path)
2464
dir_id = dir_ids[dirname]
2466
dir_id = osutils.basename(dirname) + '-id'
2468
ie = inventory.InventoryDirectory(file_id, basename, dir_id)
2469
dir_ids[path] = file_id
2471
ie = inventory.InventoryFile(file_id, basename, dir_id)
2474
ie.revision = rev_id
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:
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
2488
inv.add(self.path_to_ie(path, file_id, rev_id, dir_ids))
2489
return revisiontree.InventoryRevisionTree(_Repo(), inv, rev_id)
2491
def create_empty_dirstate(self):
2492
fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
2493
self.addCleanup(os.remove, path)
2495
state = dirstate.DirState.initialize(path)
2496
self.addCleanup(state.unlock)
2499
def create_inv_delta(self, delta, rev_id):
2500
"""Translate a 'delta shape' into an actual InventoryDelta"""
2501
dir_ids = {'': 'root-id'}
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))
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))
2515
def assertUpdate(self, active, basis, target):
2516
"""Assert that update_basis_by_delta works how we want.
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.
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')
2532
dirstate_tree = workingtree_4.DirStateRevisionTree(state,
2534
# The target now that delta has been applied should match the
2536
self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
2537
# And the dirblock state should be identical to the state if we created
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)
2545
def assertBadDelta(self, active, basis, delta):
2546
"""Test that we raise InconsistentDelta when appropriate.
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')
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')
2566
## state.update_basis_by_delta(inv_delta, 'target')
2567
## except errors.InconsistentDelta, e:
2568
## import pdb; pdb.set_trace()
2570
## import pdb; pdb.set_trace()
2571
self.assertTrue(state._changes_aborted)
2573
def test_remove_file_matching_active_state(self):
2574
state = self.assertUpdate(
2576
basis =[('file', 'file-id')],
2580
def test_remove_file_present_in_active_state(self):
2581
state = self.assertUpdate(
2582
active=[('file', 'file-id')],
2583
basis =[('file', 'file-id')],
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')],
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')],
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')],
2609
def test_add_file_matching_active_state(self):
2610
state = self.assertUpdate(
2611
active=[('file', 'file-id')],
2613
target=[('file', 'file-id')],
2616
def test_add_file_missing_in_active_state(self):
2617
state = self.assertUpdate(
2620
target=[('file', 'file-id')],
2623
def test_add_file_elsewhere_in_active_state(self):
2624
state = self.assertUpdate(
2625
active=[('other-file', 'file-id')],
2627
target=[('file', 'file-id')],
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')],
2635
target=[('file', 'file-id')],
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')],
2645
def test_rename_file_missing_in_active_state(self):
2646
state = self.assertUpdate(
2648
basis =[('file', 'file-id')],
2649
target=[('other-file', 'file-id')],
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')],
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')],
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')],
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')])
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')])
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
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')])
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')])
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')])
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')])
2753
def test_invalid_parent_missing(self):
2754
state = self.assertBadDelta(
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')],
2763
delta=[(None, 'path/path2', 'file-id')])
2764
state = self.assertBadDelta(
2765
active=[('path/', 'path-id'),
2766
('path/path2', 'file-id')],
2768
delta=[(None, 'path/path2', 'file-id')])
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'),
2776
basis= [('dir/', 'A-id'),
2778
target=[('dir/', 'C-id'),
2780
state = self.assertUpdate(# Same as target
2781
active=[('dir/', 'C-id'),
2783
basis= [('dir/', 'A-id'),
2785
target=[('dir/', 'C-id'),
2787
state = self.assertUpdate(# empty active
2789
basis= [('dir/', 'A-id'),
2791
target=[('dir/', 'C-id'),
2793
state = self.assertUpdate(# different active
2794
active=[('dir/', 'D-id'),
2796
basis= [('dir/', 'A-id'),
2798
target=[('dir/', 'C-id'),
2801
def test_parent_child_swap(self):
2802
state = self.assertUpdate(# Same as basis
2803
active=[('A/', 'A-id'),
2806
basis= [('A/', 'A-id'),
2809
target=[('A/', 'B-id'),
2812
state = self.assertUpdate(# Same as target
2813
active=[('A/', 'B-id'),
2816
basis= [('A/', 'A-id'),
2819
target=[('A/', 'B-id'),
2822
state = self.assertUpdate(# empty active
2824
basis= [('A/', 'A-id'),
2827
target=[('A/', 'B-id'),
2830
state = self.assertUpdate(# different active
2831
active=[('D/', 'A-id'),
2834
basis= [('A/', 'A-id'),
2837
target=[('A/', 'B-id'),
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')])
2864
def test_change_file_absent_in_active(self):
2865
state = self.assertUpdate(
2867
basis= [('file', 'file-id')],
2868
target=[('file', 'file-id')])
2870
def test_invalid_changed_file(self):
2871
state = self.assertBadDelta( # Not present in basis
2872
active=[('file', 'file-id')],
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')])