~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-07-22 18:09:04 UTC
  • mfrom: (2485.8.63 bzr.connection.sharing)
  • Revision ID: pqm@pqm.ubuntu.com-20070722180904-wy7y7oyi32wbghgf
Transport connection sharing

Show diffs side-by-side

added added

removed removed

Lines of Context:
189
189
              c
190
190
              d/
191
191
                e
 
192
            b-c
192
193
            f
193
194
        """
194
195
        tree = self.make_branch_and_tree('tree')
195
 
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'f']
196
 
        file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'f-id']
 
196
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'b-c', 'f']
 
197
        file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'b-c-id', 'f-id']
197
198
        self.build_tree(['tree/' + p for p in paths])
198
199
        tree.set_root_id('TREE_ROOT')
199
200
        tree.add([p.rstrip('/') for p in paths], file_ids)
200
201
        tree.commit('initial', rev_id='rev-1')
201
202
        revision_id = 'rev-1'
202
203
        # a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
203
 
        t = self.get_transport().clone('tree')
 
204
        t = self.get_transport('tree')
204
205
        a_text = t.get_bytes('a')
205
206
        a_sha = osutils.sha_string(a_text)
206
207
        a_len = len(a_text)
214
215
        e_text = t.get_bytes('b/d/e')
215
216
        e_sha = osutils.sha_string(e_text)
216
217
        e_len = len(e_text)
 
218
        b_c_text = t.get_bytes('b-c')
 
219
        b_c_sha = osutils.sha_string(b_c_text)
 
220
        b_c_len = len(b_c_text)
217
221
        # f_packed_stat = dirstate.pack_stat(os.stat('tree/f'))
218
222
        f_text = t.get_bytes('f')
219
223
        f_sha = osutils.sha_string(f_text)
244
248
                      ('f', '', 0, False, null_stat),
245
249
                      ('f', e_sha, e_len, False, revision_id),
246
250
                     ]),
 
251
            'b-c':(('', 'b-c', 'b-c-id'), [
 
252
                      ('f', '', 0, False, null_stat),
 
253
                      ('f', b_c_sha, b_c_len, False, revision_id),
 
254
                     ]),
247
255
            'f':(('', 'f', 'f-id'), [
248
256
                  ('f', '', 0, False, null_stat),
249
257
                  ('f', f_sha, f_len, False, revision_id),
276
284
        tree, state, expected = self.create_basic_dirstate()
277
285
        # Now we will just remove and add every file so we get an extra entry
278
286
        # per entry. Unversion in reverse order so we handle subdirs
279
 
        tree.unversion(['f-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
280
 
        tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f'],
281
 
                 ['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'f-id2'])
 
287
        tree.unversion(['f-id', 'b-c-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
 
288
        tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f'],
 
289
                 ['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'b-c-id2', 'f-id2'])
282
290
 
283
291
        # Update the expected dictionary.
284
 
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
 
292
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f']:
285
293
            orig = expected[path]
286
294
            path2 = path + '2'
287
295
            # This record was deleted in the current tree
344
352
            state.lock_read()
345
353
        return tree, state, expected
346
354
 
 
355
 
347
356
class TestTreeToDirState(TestCaseWithDirState):
348
357
 
349
358
    def test_empty_to_dirstate(self):
558
567
            self.assertEqual('', entry[1][0][1])
559
568
            # We should have a real entry.
560
569
            self.assertNotEqual((None, None), entry)
 
570
            # Make sure everything is old enough
 
571
            state._sha_cutoff_time()
 
572
            state._cutoff_time += 10
561
573
            sha1sum = state.update_entry(entry, 'a-file', os.lstat('a-file'))
562
574
            # We should have gotten a real sha1
563
575
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
688
700
            # This will unlock it
689
701
            self.check_state_with_reopen(expected_result, state)
690
702
 
 
703
    def test_set_state_from_inventory_mixed_paths(self):
 
704
        tree1 = self.make_branch_and_tree('tree1')
 
705
        self.build_tree(['tree1/a/', 'tree1/a/b/', 'tree1/a-b/',
 
706
                         'tree1/a/b/foo', 'tree1/a-b/bar'])
 
707
        tree1.lock_write()
 
708
        try:
 
709
            tree1.add(['a', 'a/b', 'a-b', 'a/b/foo', 'a-b/bar'],
 
710
                      ['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
 
711
            tree1.commit('rev1', rev_id='rev1')
 
712
            root_id = tree1.get_root_id()
 
713
            inv = tree1.inventory
 
714
        finally:
 
715
            tree1.unlock()
 
716
        expected_result1 = [('', '', root_id, 'd'),
 
717
                            ('', 'a', 'a-id', 'd'),
 
718
                            ('', 'a-b', 'a-b-id', 'd'),
 
719
                            ('a', 'b', 'b-id', 'd'),
 
720
                            ('a/b', 'foo', 'foo-id', 'f'),
 
721
                            ('a-b', 'bar', 'bar-id', 'f'),
 
722
                           ]
 
723
        expected_result2 = [('', '', root_id, 'd'),
 
724
                            ('', 'a', 'a-id', 'd'),
 
725
                            ('', 'a-b', 'a-b-id', 'd'),
 
726
                            ('a-b', 'bar', 'bar-id', 'f'),
 
727
                           ]
 
728
        state = dirstate.DirState.initialize('dirstate')
 
729
        try:
 
730
            state.set_state_from_inventory(inv)
 
731
            values = []
 
732
            for entry in state._iter_entries():
 
733
                values.append(entry[0] + entry[1][0][:1])
 
734
            self.assertEqual(expected_result1, values)
 
735
            del inv['b-id']
 
736
            state.set_state_from_inventory(inv)
 
737
            values = []
 
738
            for entry in state._iter_entries():
 
739
                values.append(entry[0] + entry[1][0][:1])
 
740
            self.assertEqual(expected_result2, values)
 
741
        finally:
 
742
            state.unlock()
 
743
 
691
744
    def test_set_path_id_no_parents(self):
692
745
        """The id of a path can be changed trivally with no parents."""
693
746
        state = dirstate.DirState.initialize('dirstate')
1421
1474
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1422
1475
                         link_or_sha1)
1423
1476
 
1424
 
        # The dirblock entry should be updated with the new info
1425
 
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
 
1477
        # The dirblock entry should not cache the file's sha1
 
1478
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1426
1479
                         entry[1])
1427
1480
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1428
1481
                         state._dirblock_state)
1447
1500
                         link_or_sha1)
1448
1501
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1449
1502
                         state._dirblock_state)
 
1503
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
 
1504
                         entry[1])
1450
1505
        state.save()
1451
1506
 
1452
1507
        # However, if we move the clock forward so the file is considered
1453
 
        # "stable", it should just returned the cached value.
1454
 
        state.adjust_time(20)
 
1508
        # "stable", it should just cache the value.
 
1509
        state.adjust_time(+20)
1455
1510
        link_or_sha1 = state.update_entry(entry, abspath='a',
1456
1511
                                          stat_value=stat_value)
1457
1512
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1458
1513
                         link_or_sha1)
1459
1514
        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1460
1515
                          ('sha1', 'a'), ('is_exec', mode, False),
 
1516
                          ('sha1', 'a'), ('is_exec', mode, False),
1461
1517
                         ], state._log)
 
1518
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
 
1519
                         entry[1])
1462
1520
 
1463
 
    def test_update_entry_no_stat_value(self):
1464
 
        """Passing the stat_value is optional."""
1465
 
        state, entry = self.get_state_with_a()
1466
 
        state.adjust_time(-10) # Make sure the file looks new
1467
 
        self.build_tree(['a'])
1468
 
        # Add one where we don't provide the stat or sha already
1469
 
        link_or_sha1 = state.update_entry(entry, abspath='a')
 
1521
        # Subsequent calls will just return the cached value
 
1522
        link_or_sha1 = state.update_entry(entry, abspath='a',
 
1523
                                          stat_value=stat_value)
1470
1524
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1471
1525
                         link_or_sha1)
1472
 
        stat_value = os.lstat('a')
1473
 
        self.assertEqual([('lstat', 'a'), ('sha1', 'a'),
1474
 
                          ('is_exec', stat_value.st_mode, False),
 
1526
        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
 
1527
                          ('sha1', 'a'), ('is_exec', mode, False),
 
1528
                          ('sha1', 'a'), ('is_exec', mode, False),
1475
1529
                         ], state._log)
 
1530
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
 
1531
                         entry[1])
1476
1532
 
1477
1533
    def test_update_entry_symlink(self):
1478
1534
        """Update entry should read symlinks."""
1492
1548
                                          stat_value=stat_value)
1493
1549
        self.assertEqual('target', link_or_sha1)
1494
1550
        self.assertEqual([('read_link', 'a', '')], state._log)
1495
 
        # Dirblock is updated
1496
 
        self.assertEqual([('l', link_or_sha1, 6, False, packed_stat)],
 
1551
        # Dirblock is not updated (the link is too new)
 
1552
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1497
1553
                         entry[1])
1498
1554
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1499
1555
                         state._dirblock_state)
1503
1559
                                          stat_value=stat_value)
1504
1560
        self.assertEqual('target', link_or_sha1)
1505
1561
        self.assertEqual([('read_link', 'a', ''),
1506
 
                          ('read_link', 'a', 'target'),
 
1562
                          ('read_link', 'a', ''),
1507
1563
                         ], state._log)
 
1564
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
 
1565
                         entry[1])
1508
1566
        state.adjust_time(+20) # Skip into the future, all files look old
1509
1567
        link_or_sha1 = state.update_entry(entry, abspath='a',
1510
1568
                                          stat_value=stat_value)
1511
1569
        self.assertEqual('target', link_or_sha1)
1512
 
        # There should not be a new read_link call.
1513
 
        # (this is a weak assertion, because read_link is fairly inexpensive,
1514
 
        # versus the number of symlinks that we would have)
1515
 
        self.assertEqual([('read_link', 'a', ''),
1516
 
                          ('read_link', 'a', 'target'),
1517
 
                         ], state._log)
 
1570
        # We need to re-read the link because only now can we cache it
 
1571
        self.assertEqual([('read_link', 'a', ''),
 
1572
                          ('read_link', 'a', ''),
 
1573
                          ('read_link', 'a', ''),
 
1574
                         ], state._log)
 
1575
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
 
1576
                         entry[1])
 
1577
 
 
1578
        # Another call won't re-read the link
 
1579
        self.assertEqual([('read_link', 'a', ''),
 
1580
                          ('read_link', 'a', ''),
 
1581
                          ('read_link', 'a', ''),
 
1582
                         ], state._log)
 
1583
        link_or_sha1 = state.update_entry(entry, abspath='a',
 
1584
                                          stat_value=stat_value)
 
1585
        self.assertEqual('target', link_or_sha1)
 
1586
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
 
1587
                         entry[1])
 
1588
 
 
1589
    def do_update_entry(self, state, entry, abspath):
 
1590
        stat_value = os.lstat(abspath)
 
1591
        return state.update_entry(entry, abspath, stat_value)
1518
1592
 
1519
1593
    def test_update_entry_dir(self):
1520
1594
        state, entry = self.get_state_with_a()
1521
1595
        self.build_tree(['a/'])
1522
 
        self.assertIs(None, state.update_entry(entry, 'a'))
 
1596
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
 
1597
 
 
1598
    def test_update_entry_dir_unchanged(self):
 
1599
        state, entry = self.get_state_with_a()
 
1600
        self.build_tree(['a/'])
 
1601
        state.adjust_time(+20)
 
1602
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
 
1603
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
1604
                         state._dirblock_state)
 
1605
        state.save()
 
1606
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1607
                         state._dirblock_state)
 
1608
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
 
1609
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1610
                         state._dirblock_state)
 
1611
 
 
1612
    def test_update_entry_file_unchanged(self):
 
1613
        state, entry = self.get_state_with_a()
 
1614
        self.build_tree(['a'])
 
1615
        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
 
1616
        state.adjust_time(+20)
 
1617
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
 
1618
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
1619
                         state._dirblock_state)
 
1620
        state.save()
 
1621
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1622
                         state._dirblock_state)
 
1623
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
 
1624
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
1625
                         state._dirblock_state)
1523
1626
 
1524
1627
    def create_and_test_file(self, state, entry):
1525
1628
        """Create a file at 'a' and verify the state finds it.
1531
1634
        stat_value = os.lstat('a')
1532
1635
        packed_stat = dirstate.pack_stat(stat_value)
1533
1636
 
1534
 
        link_or_sha1 = state.update_entry(entry, abspath='a')
 
1637
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1535
1638
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1536
1639
                         link_or_sha1)
1537
1640
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1548
1651
        stat_value = os.lstat('a')
1549
1652
        packed_stat = dirstate.pack_stat(stat_value)
1550
1653
 
1551
 
        link_or_sha1 = state.update_entry(entry, abspath='a')
 
1654
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1552
1655
        self.assertIs(None, link_or_sha1)
1553
1656
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1554
1657
 
1569
1672
        stat_value = os.lstat('a')
1570
1673
        packed_stat = dirstate.pack_stat(stat_value)
1571
1674
 
1572
 
        link_or_sha1 = state.update_entry(entry, abspath='a')
 
1675
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1573
1676
        self.assertEqual('path/to/foo', link_or_sha1)
1574
1677
        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1575
1678
                         entry[1])
1576
1679
        return packed_stat
1577
1680
 
1578
 
    def test_update_missing_file(self):
1579
 
        state, entry = self.get_state_with_a()
1580
 
        packed_stat = self.create_and_test_file(state, entry)
1581
 
        # Now if we delete the file, update_entry should recover and
1582
 
        # return None.
1583
 
        os.remove('a')
1584
 
        self.assertIs(None, state.update_entry(entry, abspath='a'))
1585
 
        # And the record shouldn't be changed.
1586
 
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1587
 
        self.assertEqual([('f', digest, 14, False, packed_stat)],
1588
 
                         entry[1])
1589
 
 
1590
 
    def test_update_missing_dir(self):
1591
 
        state, entry = self.get_state_with_a()
1592
 
        packed_stat = self.create_and_test_dir(state, entry)
1593
 
        # Now if we delete the directory, update_entry should recover and
1594
 
        # return None.
1595
 
        os.rmdir('a')
1596
 
        self.assertIs(None, state.update_entry(entry, abspath='a'))
1597
 
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1598
 
 
1599
 
    def test_update_missing_symlink(self):
1600
 
        if not osutils.has_symlinks():
1601
 
            # PlatformDeficiency / TestSkipped
1602
 
            raise TestSkipped("No symlink support")
1603
 
        state, entry = self.get_state_with_a()
1604
 
        packed_stat = self.create_and_test_symlink(state, entry)
1605
 
        os.remove('a')
1606
 
        self.assertIs(None, state.update_entry(entry, abspath='a'))
1607
 
        # And the record shouldn't be changed.
1608
 
        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1609
 
                         entry[1])
1610
 
 
1611
1681
    def test_update_file_to_dir(self):
1612
1682
        """If a file changes to a directory we return None for the sha.
1613
1683
        We also update the inventory record.
1614
1684
        """
1615
1685
        state, entry = self.get_state_with_a()
 
1686
        # The file sha1 won't be cached unless the file is old
 
1687
        state.adjust_time(+10)
1616
1688
        self.create_and_test_file(state, entry)
1617
1689
        os.remove('a')
1618
1690
        self.create_and_test_dir(state, entry)
1623
1695
            # PlatformDeficiency / TestSkipped
1624
1696
            raise TestSkipped("No symlink support")
1625
1697
        state, entry = self.get_state_with_a()
 
1698
        # The file sha1 won't be cached unless the file is old
 
1699
        state.adjust_time(+10)
1626
1700
        self.create_and_test_file(state, entry)
1627
1701
        os.remove('a')
1628
1702
        self.create_and_test_symlink(state, entry)
1630
1704
    def test_update_dir_to_file(self):
1631
1705
        """Directory becoming a file updates the entry."""
1632
1706
        state, entry = self.get_state_with_a()
 
1707
        # The file sha1 won't be cached unless the file is old
 
1708
        state.adjust_time(+10)
1633
1709
        self.create_and_test_dir(state, entry)
1634
1710
        os.rmdir('a')
1635
1711
        self.create_and_test_file(state, entry)
1640
1716
            # PlatformDeficiency / TestSkipped
1641
1717
            raise TestSkipped("No symlink support")
1642
1718
        state, entry = self.get_state_with_a()
 
1719
        # The symlink target won't be cached if it isn't old
 
1720
        state.adjust_time(+10)
1643
1721
        self.create_and_test_dir(state, entry)
1644
1722
        os.rmdir('a')
1645
1723
        self.create_and_test_symlink(state, entry)
1649
1727
        if not has_symlinks():
1650
1728
            raise TestSkipped("No symlink support")
1651
1729
        state, entry = self.get_state_with_a()
 
1730
        # The symlink and file info won't be cached unless old
 
1731
        state.adjust_time(+10)
1652
1732
        self.create_and_test_symlink(state, entry)
1653
1733
        os.remove('a')
1654
1734
        self.create_and_test_file(state, entry)
1658
1738
        if not has_symlinks():
1659
1739
            raise TestSkipped("No symlink support")
1660
1740
        state, entry = self.get_state_with_a()
 
1741
        # The symlink target won't be cached if it isn't old
 
1742
        state.adjust_time(+10)
1661
1743
        self.create_and_test_symlink(state, entry)
1662
1744
        os.remove('a')
1663
1745
        self.create_and_test_dir(state, entry)
1677
1759
        packed_stat = dirstate.pack_stat(stat_value)
1678
1760
 
1679
1761
        state.adjust_time(-10) # Make sure everything is new
1680
 
        # Make sure it wants to kkkkkkkk
1681
1762
        state.update_entry(entry, abspath='a', stat_value=stat_value)
1682
1763
 
1683
1764
        # The row is updated, but the executable bit stays set.
 
1765
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
 
1766
                         entry[1])
 
1767
 
 
1768
        # Make the disk object look old enough to cache
 
1769
        state.adjust_time(+20)
1684
1770
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
 
1771
        state.update_entry(entry, abspath='a', stat_value=stat_value)
1685
1772
        self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
1686
1773
 
1687
1774
 
1740
1827
class TestBisect(TestCaseWithDirState):
1741
1828
    """Test the ability to bisect into the disk format."""
1742
1829
 
1743
 
 
1744
1830
    def assertBisect(self, expected_map, map_keys, state, paths):
1745
1831
        """Assert that bisecting for paths returns the right result.
1746
1832
 
1751
1837
                      (dir, name) tuples, and sorted according to how _bisect
1752
1838
                      requires.
1753
1839
        """
1754
 
        dir_names = sorted(osutils.split(p) for p in paths)
1755
 
        result = state._bisect(dir_names)
 
1840
        result = state._bisect(paths)
1756
1841
        # For now, results are just returned in whatever order we read them.
1757
1842
        # We could sort by (dir, name, file_id) or something like that, but in
1758
1843
        # the end it would still be fairly arbitrary, and we don't want the
1759
1844
        # extra overhead if we can avoid it. So sort everything to make sure
1760
1845
        # equality is true
1761
 
        assert len(map_keys) == len(dir_names)
 
1846
        assert len(map_keys) == len(paths)
1762
1847
        expected = {}
1763
 
        for dir_name, keys in zip(dir_names, map_keys):
 
1848
        for path, keys in zip(paths, map_keys):
1764
1849
            if keys is None:
1765
1850
                # This should not be present in the output
1766
1851
                continue
1767
 
            expected[dir_name] = sorted(expected_map[k] for k in keys)
 
1852
            expected[path] = sorted(expected_map[k] for k in keys)
1768
1853
 
1769
 
        for dir_name in result:
1770
 
            result[dir_name].sort()
 
1854
        # The returned values are just arranged randomly based on when they
 
1855
        # were read, for testing, make sure it is properly sorted.
 
1856
        for path in result:
 
1857
            result[path].sort()
1771
1858
 
1772
1859
        self.assertEqual(expected, result)
1773
1860
 
1810
1897
            dir_name_id, trees_info = entry
1811
1898
            expected[dir_name_id] = trees_info
1812
1899
 
1813
 
        dir_names = sorted(osutils.split(p) for p in paths)
1814
 
        result = state._bisect_recursive(dir_names)
 
1900
        result = state._bisect_recursive(paths)
1815
1901
 
1816
1902
        self.assertEqual(expected, result)
1817
1903
 
1826
1912
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
1827
1913
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1828
1914
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
 
1915
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
1829
1916
        self.assertBisect(expected, [['f']], state, ['f'])
1830
1917
 
1831
1918
    def test_bisect_multi(self):
1834
1921
        # Bisect should be capable of finding multiple entries at the same time
1835
1922
        self.assertBisect(expected, [['a'], ['b'], ['f']],
1836
1923
                          state, ['a', 'b', 'f'])
1837
 
        # ('', 'f') sorts before the others
1838
1924
        self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1839
 
                          state, ['b/d', 'b/d/e', 'f'])
 
1925
                          state, ['f', 'b/d', 'b/d/e'])
 
1926
        self.assertBisect(expected, [['b'], ['b-c'], ['b/c']],
 
1927
                          state, ['b', 'b-c', 'b/c'])
1840
1928
 
1841
1929
    def test_bisect_one_page(self):
1842
1930
        """Test bisect when there is only 1 page to read"""
1848
1936
        self.assertBisect(expected,[['b/c']], state, ['b/c'])
1849
1937
        self.assertBisect(expected,[['b/d']], state, ['b/d'])
1850
1938
        self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
 
1939
        self.assertBisect(expected,[['b-c']], state, ['b-c'])
1851
1940
        self.assertBisect(expected,[['f']], state, ['f'])
1852
1941
        self.assertBisect(expected,[['a'], ['b'], ['f']],
1853
1942
                          state, ['a', 'b', 'f'])
1854
 
        # ('', 'f') sorts before the others
1855
 
        self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
 
1943
        self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
1856
1944
                          state, ['b/d', 'b/d/e', 'f'])
 
1945
        self.assertBisect(expected, [['b'], ['b/c'], ['b-c']],
 
1946
                          state, ['b', 'b/c', 'b-c'])
1857
1947
 
1858
1948
    def test_bisect_duplicate_paths(self):
1859
1949
        """When bisecting for a path, handle multiple entries."""
1867
1957
        self.assertBisect(expected, [['b/d', 'b/d2']], state, ['b/d'])
1868
1958
        self.assertBisect(expected, [['b/d/e', 'b/d/e2']],
1869
1959
                          state, ['b/d/e'])
 
1960
        self.assertBisect(expected, [['b-c', 'b-c2']], state, ['b-c'])
1870
1961
        self.assertBisect(expected, [['f', 'f2']], state, ['f'])
1871
1962
 
1872
1963
    def test_bisect_page_size_too_small(self):
1879
1970
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
1880
1971
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1881
1972
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
 
1973
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
1882
1974
        self.assertBisect(expected, [['f']], state, ['f'])
1883
1975
 
1884
1976
    def test_bisect_missing(self):
1887
1979
        self.assertBisect(expected, [None], state, ['foo'])
1888
1980
        self.assertBisect(expected, [None], state, ['b/foo'])
1889
1981
        self.assertBisect(expected, [None], state, ['bar/foo'])
 
1982
        self.assertBisect(expected, [None], state, ['b-c/foo'])
1890
1983
 
1891
1984
        self.assertBisect(expected, [['a'], None, ['b/d']],
1892
1985
                          state, ['a', 'foo', 'b/d'])
1908
2001
    def test_bisect_dirblocks(self):
1909
2002
        tree, state, expected = self.create_duplicated_dirstate()
1910
2003
        self.assertBisectDirBlocks(expected,
1911
 
            [['', 'a', 'a2', 'b', 'b2', 'f', 'f2']], state, [''])
 
2004
            [['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2']],
 
2005
            state, [''])
1912
2006
        self.assertBisectDirBlocks(expected,
1913
2007
            [['b/c', 'b/c2', 'b/d', 'b/d2']], state, ['b'])
1914
2008
        self.assertBisectDirBlocks(expected,
1915
2009
            [['b/d/e', 'b/d/e2']], state, ['b/d'])
1916
2010
        self.assertBisectDirBlocks(expected,
1917
 
            [['', 'a', 'a2', 'b', 'b2', 'f', 'f2'],
 
2011
            [['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2'],
1918
2012
             ['b/c', 'b/c2', 'b/d', 'b/d2'],
1919
2013
             ['b/d/e', 'b/d/e2'],
1920
2014
            ], state, ['', 'b', 'b/d'])
1935
2029
        self.assertBisectRecursive(expected, ['a'], state, ['a'])
1936
2030
        self.assertBisectRecursive(expected, ['b/c'], state, ['b/c'])
1937
2031
        self.assertBisectRecursive(expected, ['b/d/e'], state, ['b/d/e'])
 
2032
        self.assertBisectRecursive(expected, ['b-c'], state, ['b-c'])
1938
2033
        self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
1939
2034
                                   state, ['b/d'])
1940
2035
        self.assertBisectRecursive(expected, ['b', 'b/c', 'b/d', 'b/d/e'],
1941
2036
                                   state, ['b'])
1942
 
        self.assertBisectRecursive(expected, ['', 'a', 'b', 'f', 'b/c',
 
2037
        self.assertBisectRecursive(expected, ['', 'a', 'b', 'b-c', 'f', 'b/c',
1943
2038
                                              'b/d', 'b/d/e'],
1944
2039
                                   state, [''])
1945
2040
 
1969
2064
                                   state, ['b'])
1970
2065
 
1971
2066
 
1972
 
class TestBisectDirblock(TestCase):
1973
 
    """Test that bisect_dirblock() returns the expected values.
1974
 
 
1975
 
    bisect_dirblock is intended to work like bisect.bisect_left() except it
1976
 
    knows it is working on dirblocks and that dirblocks are sorted by ('path',
1977
 
    'to', 'foo') chunks rather than by raw 'path/to/foo'.
1978
 
    """
1979
 
 
1980
 
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
1981
 
        """Assert that bisect_split works like bisect_left on the split paths.
1982
 
 
1983
 
        :param dirblocks: A list of (path, [info]) pairs.
1984
 
        :param split_dirblocks: A list of ((split, path), [info]) pairs.
1985
 
        :param path: The path we are indexing.
1986
 
 
1987
 
        All other arguments will be passed along.
1988
 
        """
1989
 
        bisect_split_idx = dirstate.bisect_dirblock(dirblocks, path,
1990
 
                                                 *args, **kwargs)
1991
 
        split_dirblock = (path.split('/'), [])
1992
 
        bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
1993
 
                                             *args)
1994
 
        self.assertEqual(bisect_left_idx, bisect_split_idx,
1995
 
                         'bisect_split disagreed. %s != %s'
1996
 
                         ' for key %s'
1997
 
                         % (bisect_left_idx, bisect_split_idx, path)
1998
 
                         )
1999
 
 
2000
 
    def paths_to_dirblocks(self, paths):
2001
 
        """Convert a list of paths into dirblock form.
2002
 
 
2003
 
        Also, ensure that the paths are in proper sorted order.
2004
 
        """
2005
 
        dirblocks = [(path, []) for path in paths]
2006
 
        split_dirblocks = [(path.split('/'), []) for path in paths]
2007
 
        self.assertEqual(sorted(split_dirblocks), split_dirblocks)
2008
 
        return dirblocks, split_dirblocks
2009
 
 
2010
 
    def test_simple(self):
2011
 
        """In the simple case it works just like bisect_left"""
2012
 
        paths = ['', 'a', 'b', 'c', 'd']
2013
 
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2014
 
        for path in paths:
2015
 
            self.assertBisect(dirblocks, split_dirblocks, path)
2016
 
        self.assertBisect(dirblocks, split_dirblocks, '_')
2017
 
        self.assertBisect(dirblocks, split_dirblocks, 'aa')
2018
 
        self.assertBisect(dirblocks, split_dirblocks, 'bb')
2019
 
        self.assertBisect(dirblocks, split_dirblocks, 'cc')
2020
 
        self.assertBisect(dirblocks, split_dirblocks, 'dd')
2021
 
        self.assertBisect(dirblocks, split_dirblocks, 'a/a')
2022
 
        self.assertBisect(dirblocks, split_dirblocks, 'b/b')
2023
 
        self.assertBisect(dirblocks, split_dirblocks, 'c/c')
2024
 
        self.assertBisect(dirblocks, split_dirblocks, 'd/d')
2025
 
 
2026
 
    def test_involved(self):
2027
 
        """This is where bisect_left diverges slightly."""
2028
 
        paths = ['', 'a',
2029
 
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2030
 
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2031
 
                 'a-a', 'a-z',
2032
 
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2033
 
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2034
 
                 'z-a', 'z-z',
2035
 
                ]
2036
 
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2037
 
        for path in paths:
2038
 
            self.assertBisect(dirblocks, split_dirblocks, path)
2039
 
 
2040
 
    def test_involved_cached(self):
2041
 
        """This is where bisect_left diverges slightly."""
2042
 
        paths = ['', 'a',
2043
 
                 'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2044
 
                 'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2045
 
                 'a-a', 'a-z',
2046
 
                 'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2047
 
                 'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2048
 
                 'z-a', 'z-z',
2049
 
                ]
2050
 
        cache = {}
2051
 
        dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2052
 
        for path in paths:
2053
 
            self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
2054
 
 
2055
 
 
2056
2067
class TestDirstateValidation(TestCaseWithDirState):
2057
2068
 
2058
2069
    def test_validate_correct_dirstate(self):
2108
2119
            state._validate)
2109
2120
        self.assertContainsRe(str(e),
2110
2121
            'file a-id is absent in row')
 
2122