1314
1218
self.assertRaises(errors.BzrError,
1315
1219
state.add, '..', 'ass-id', 'directory', None, None)
1317
def test_set_state_with_rename_b_a_bug_395556(self):
1318
# bug 395556 uncovered a bug where the dirstate ends up with a false
1319
# relocation record - in a tree with no parents there should be no
1320
# absent or relocated records. This then leads to further corruption
1321
# when a commit occurs, as the incorrect relocation gathers an
1322
# incorrect absent in tree 1, and future changes go to pot.
1323
tree1 = self.make_branch_and_tree('tree1')
1324
self.build_tree(['tree1/b'])
1327
tree1.add(['b'], ['b-id'])
1328
root_id = tree1.get_root_id()
1329
inv = tree1.inventory
1330
state = dirstate.DirState.initialize('dirstate')
1332
# Set the initial state with 'b'
1333
state.set_state_from_inventory(inv)
1334
inv.rename('b-id', root_id, 'a')
1335
# Set the new state with 'a', which currently corrupts.
1336
state.set_state_from_inventory(inv)
1337
expected_result1 = [('', '', root_id, 'd'),
1338
('', 'a', 'b-id', 'f'),
1341
for entry in state._iter_entries():
1342
values.append(entry[0] + entry[1][0][:1])
1343
self.assertEqual(expected_result1, values)
1350
class TestDirStateHashUpdates(TestCaseWithDirState):
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)
1357
def _read_state_content(self, state):
1358
"""Read the content of the dirstate file.
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.
1367
state._state_file.seek(0)
1368
return state._state_file.read()
1370
def test_worth_saving_limit_avoids_writing(self):
1371
tree = self.make_branch_and_tree('.')
1372
self.build_tree(['c', 'd'])
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)
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)
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))
1401
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1402
state._dirblock_state)
1403
self.assertEqual(0, len(state._known_hash_changes))
1406
1222
class TestGetLines(TestCaseWithDirState):
2438
2253
self.assertTrue(len(statvalue) >= 10)
2439
2254
self.assertEqual(len(text), statvalue.st_size)
2440
2255
self.assertEqual(expected_sha, sha1)
2443
class _Repo(object):
2444
"""A minimal api to get InventoryRevisionTree to work."""
2447
default_format = bzrdir.format_registry.make_bzrdir('default')
2448
self._format = default_format.repository_format
2450
def lock_read(self):
2457
class TestUpdateBasisByDelta(tests.TestCase):
2459
def path_to_ie(self, path, file_id, rev_id, dir_ids):
2460
if path.endswith('/'):
2465
dirname, basename = osutils.split(path)
2467
dir_id = dir_ids[dirname]
2469
dir_id = osutils.basename(dirname) + '-id'
2471
ie = inventory.InventoryDirectory(file_id, basename, dir_id)
2472
dir_ids[path] = file_id
2474
ie = inventory.InventoryFile(file_id, basename, dir_id)
2477
ie.revision = rev_id
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:
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
2491
inv.add(self.path_to_ie(path, file_id, rev_id, dir_ids))
2492
return revisiontree.InventoryRevisionTree(_Repo(), inv, rev_id)
2494
def create_empty_dirstate(self):
2495
fd, path = tempfile.mkstemp(prefix='bzr-dirstate')
2496
self.addCleanup(os.remove, path)
2498
state = dirstate.DirState.initialize(path)
2499
self.addCleanup(state.unlock)
2502
def create_inv_delta(self, delta, rev_id):
2503
"""Translate a 'delta shape' into an actual InventoryDelta"""
2504
dir_ids = {'': 'root-id'}
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))
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))
2518
def assertUpdate(self, active, basis, target):
2519
"""Assert that update_basis_by_delta works how we want.
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.
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.inventory,
2531
[('basis', basis_tree)], [])
2532
delta = target_tree.inventory._make_delta(basis_tree.inventory)
2533
state.update_basis_by_delta(delta, 'target')
2535
dirstate_tree = workingtree_4.DirStateRevisionTree(state,
2537
# The target now that delta has been applied should match the
2539
self.assertEqual([], list(dirstate_tree.iter_changes(target_tree)))
2540
# And the dirblock state should be identical to the state if we created
2542
state2 = self.create_empty_dirstate()
2543
state2.set_state_from_scratch(active_tree.inventory,
2544
[('target', target_tree)], [])
2545
self.assertEqual(state2._dirblocks, state._dirblocks)
2548
def assertBadDelta(self, active, basis, delta):
2549
"""Test that we raise InconsistentDelta when appropriate.
2551
:param active: The active tree shape
2552
:param basis: The basis tree shape
2553
:param delta: A description of the delta to apply. Similar to the form
2554
for regular inventory deltas, but omitting the InventoryEntry.
2555
So adding a file is: (None, 'path', 'file-id')
2556
Adding a directory is: (None, 'path/', 'dir-id')
2557
Renaming a dir is: ('old/', 'new/', 'dir-id')
2560
active_tree = self.create_tree_from_shape('active', active)
2561
basis_tree = self.create_tree_from_shape('basis', basis)
2562
inv_delta = self.create_inv_delta(delta, 'target')
2563
state = self.create_empty_dirstate()
2564
state.set_state_from_scratch(active_tree.inventory,
2565
[('basis', basis_tree)], [])
2566
self.assertRaises(errors.InconsistentDelta,
2567
state.update_basis_by_delta, inv_delta, 'target')
2569
## state.update_basis_by_delta(inv_delta, 'target')
2570
## except errors.InconsistentDelta, e:
2571
## import pdb; pdb.set_trace()
2573
## import pdb; pdb.set_trace()
2574
self.assertTrue(state._changes_aborted)
2576
def test_remove_file_matching_active_state(self):
2577
state = self.assertUpdate(
2579
basis =[('file', 'file-id')],
2583
def test_remove_file_present_in_active_state(self):
2584
state = self.assertUpdate(
2585
active=[('file', 'file-id')],
2586
basis =[('file', 'file-id')],
2590
def test_remove_file_present_elsewhere_in_active_state(self):
2591
state = self.assertUpdate(
2592
active=[('other-file', 'file-id')],
2593
basis =[('file', 'file-id')],
2597
def test_remove_file_active_state_has_diff_file(self):
2598
state = self.assertUpdate(
2599
active=[('file', 'file-id-2')],
2600
basis =[('file', 'file-id')],
2604
def test_remove_file_active_state_has_diff_file_and_file_elsewhere(self):
2605
state = self.assertUpdate(
2606
active=[('file', 'file-id-2'),
2607
('other-file', 'file-id')],
2608
basis =[('file', 'file-id')],
2612
def test_add_file_matching_active_state(self):
2613
state = self.assertUpdate(
2614
active=[('file', 'file-id')],
2616
target=[('file', 'file-id')],
2619
def test_add_file_missing_in_active_state(self):
2620
state = self.assertUpdate(
2623
target=[('file', 'file-id')],
2626
def test_add_file_elsewhere_in_active_state(self):
2627
state = self.assertUpdate(
2628
active=[('other-file', 'file-id')],
2630
target=[('file', 'file-id')],
2633
def test_add_file_active_state_has_diff_file_and_file_elsewhere(self):
2634
state = self.assertUpdate(
2635
active=[('other-file', 'file-id'),
2636
('file', 'file-id-2')],
2638
target=[('file', 'file-id')],
2641
def test_rename_file_matching_active_state(self):
2642
state = self.assertUpdate(
2643
active=[('other-file', 'file-id')],
2644
basis =[('file', 'file-id')],
2645
target=[('other-file', 'file-id')],
2648
def test_rename_file_missing_in_active_state(self):
2649
state = self.assertUpdate(
2651
basis =[('file', 'file-id')],
2652
target=[('other-file', 'file-id')],
2655
def test_rename_file_present_elsewhere_in_active_state(self):
2656
state = self.assertUpdate(
2657
active=[('third', 'file-id')],
2658
basis =[('file', 'file-id')],
2659
target=[('other-file', 'file-id')],
2662
def test_rename_file_active_state_has_diff_source_file(self):
2663
state = self.assertUpdate(
2664
active=[('file', 'file-id-2')],
2665
basis =[('file', 'file-id')],
2666
target=[('other-file', 'file-id')],
2669
def test_rename_file_active_state_has_diff_target_file(self):
2670
state = self.assertUpdate(
2671
active=[('other-file', 'file-id-2')],
2672
basis =[('file', 'file-id')],
2673
target=[('other-file', 'file-id')],
2676
def test_rename_file_active_has_swapped_files(self):
2677
state = self.assertUpdate(
2678
active=[('file', 'file-id'),
2679
('other-file', 'file-id-2')],
2680
basis= [('file', 'file-id'),
2681
('other-file', 'file-id-2')],
2682
target=[('file', 'file-id-2'),
2683
('other-file', 'file-id')])
2685
def test_rename_file_basis_has_swapped_files(self):
2686
state = self.assertUpdate(
2687
active=[('file', 'file-id'),
2688
('other-file', 'file-id-2')],
2689
basis= [('file', 'file-id-2'),
2690
('other-file', 'file-id')],
2691
target=[('file', 'file-id'),
2692
('other-file', 'file-id-2')])
2694
def test_rename_directory_with_contents(self):
2695
state = self.assertUpdate( # active matches basis
2696
active=[('dir1/', 'dir-id'),
2697
('dir1/file', 'file-id')],
2698
basis= [('dir1/', 'dir-id'),
2699
('dir1/file', 'file-id')],
2700
target=[('dir2/', 'dir-id'),
2701
('dir2/file', 'file-id')])
2702
state = self.assertUpdate( # active matches target
2703
active=[('dir2/', 'dir-id'),
2704
('dir2/file', 'file-id')],
2705
basis= [('dir1/', 'dir-id'),
2706
('dir1/file', 'file-id')],
2707
target=[('dir2/', 'dir-id'),
2708
('dir2/file', 'file-id')])
2709
state = self.assertUpdate( # active empty
2711
basis= [('dir1/', 'dir-id'),
2712
('dir1/file', 'file-id')],
2713
target=[('dir2/', 'dir-id'),
2714
('dir2/file', 'file-id')])
2715
state = self.assertUpdate( # active present at other location
2716
active=[('dir3/', 'dir-id'),
2717
('dir3/file', 'file-id')],
2718
basis= [('dir1/', 'dir-id'),
2719
('dir1/file', 'file-id')],
2720
target=[('dir2/', 'dir-id'),
2721
('dir2/file', 'file-id')])
2722
state = self.assertUpdate( # active has different ids
2723
active=[('dir1/', 'dir1-id'),
2724
('dir1/file', 'file1-id'),
2725
('dir2/', 'dir2-id'),
2726
('dir2/file', 'file2-id')],
2727
basis= [('dir1/', 'dir-id'),
2728
('dir1/file', 'file-id')],
2729
target=[('dir2/', 'dir-id'),
2730
('dir2/file', 'file-id')])
2732
def test_invalid_file_not_present(self):
2733
state = self.assertBadDelta(
2734
active=[('file', 'file-id')],
2735
basis= [('file', 'file-id')],
2736
delta=[('other-file', 'file', 'file-id')])
2738
def test_invalid_new_id_same_path(self):
2739
# The bad entry comes after
2740
state = self.assertBadDelta(
2741
active=[('file', 'file-id')],
2742
basis= [('file', 'file-id')],
2743
delta=[(None, 'file', 'file-id-2')])
2744
# The bad entry comes first
2745
state = self.assertBadDelta(
2746
active=[('file', 'file-id-2')],
2747
basis=[('file', 'file-id-2')],
2748
delta=[(None, 'file', 'file-id')])
2750
def test_invalid_existing_id(self):
2751
state = self.assertBadDelta(
2752
active=[('file', 'file-id')],
2753
basis= [('file', 'file-id')],
2754
delta=[(None, 'file', 'file-id')])
2756
def test_invalid_parent_missing(self):
2757
state = self.assertBadDelta(
2760
delta=[(None, 'path/path2', 'file-id')])
2761
# Note: we force the active tree to have the directory, by knowing how
2762
# path_to_ie handles entries with missing parents
2763
state = self.assertBadDelta(
2764
active=[('path/', 'path-id')],
2766
delta=[(None, 'path/path2', 'file-id')])
2767
state = self.assertBadDelta(
2768
active=[('path/', 'path-id'),
2769
('path/path2', 'file-id')],
2771
delta=[(None, 'path/path2', 'file-id')])
2773
def test_renamed_dir_same_path(self):
2774
# We replace the parent directory, with another parent dir. But the C
2775
# file doesn't look like it has been moved.
2776
state = self.assertUpdate(# Same as basis
2777
active=[('dir/', 'A-id'),
2779
basis= [('dir/', 'A-id'),
2781
target=[('dir/', 'C-id'),
2783
state = self.assertUpdate(# Same as target
2784
active=[('dir/', 'C-id'),
2786
basis= [('dir/', 'A-id'),
2788
target=[('dir/', 'C-id'),
2790
state = self.assertUpdate(# empty active
2792
basis= [('dir/', 'A-id'),
2794
target=[('dir/', 'C-id'),
2796
state = self.assertUpdate(# different active
2797
active=[('dir/', 'D-id'),
2799
basis= [('dir/', 'A-id'),
2801
target=[('dir/', 'C-id'),
2804
def test_parent_child_swap(self):
2805
state = self.assertUpdate(# Same as basis
2806
active=[('A/', 'A-id'),
2809
basis= [('A/', 'A-id'),
2812
target=[('A/', 'B-id'),
2815
state = self.assertUpdate(# Same as target
2816
active=[('A/', 'B-id'),
2819
basis= [('A/', 'A-id'),
2822
target=[('A/', 'B-id'),
2825
state = self.assertUpdate(# empty active
2827
basis= [('A/', 'A-id'),
2830
target=[('A/', 'B-id'),
2833
state = self.assertUpdate(# different active
2834
active=[('D/', 'A-id'),
2837
basis= [('A/', 'A-id'),
2840
target=[('A/', 'B-id'),
2844
def test_change_root_id(self):
2845
state = self.assertUpdate( # same as basis
2846
active=[('', 'root-id'),
2847
('file', 'file-id')],
2848
basis= [('', 'root-id'),
2849
('file', 'file-id')],
2850
target=[('', 'target-root-id'),
2851
('file', 'file-id')])
2852
state = self.assertUpdate( # same as target
2853
active=[('', 'target-root-id'),
2854
('file', 'file-id')],
2855
basis= [('', 'root-id'),
2856
('file', 'file-id')],
2857
target=[('', 'target-root-id'),
2858
('file', 'root-id')])
2859
state = self.assertUpdate( # all different
2860
active=[('', 'active-root-id'),
2861
('file', 'file-id')],
2862
basis= [('', 'root-id'),
2863
('file', 'file-id')],
2864
target=[('', 'target-root-id'),
2865
('file', 'root-id')])
2867
def test_change_file_absent_in_active(self):
2868
state = self.assertUpdate(
2870
basis= [('file', 'file-id')],
2871
target=[('file', 'file-id')])
2873
def test_invalid_changed_file(self):
2874
state = self.assertBadDelta( # Not present in basis
2875
active=[('file', 'file-id')],
2877
delta=[('file', 'file', 'file-id')])
2878
state = self.assertBadDelta( # present at another location in basis
2879
active=[('file', 'file-id')],
2880
basis= [('other-file', 'file-id')],
2881
delta=[('file', 'file', 'file-id')])