~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: v.ladeuil+lp at free
  • Date: 2007-05-18 18:20:31 UTC
  • mto: (2485.8.44 bzr.connection.sharing)
  • mto: This revision was merged to the branch mainline in revision 2646.
  • Revision ID: v.ladeuil+lp@free.fr-20070518182031-gbg2cgidv5l20x9p
Takes Robert comments into account.

* bzrlib/transport/ftp.py:
(FtpTransport.__init__): Write a better explanation.

* bzrlib/tests/test_init.py:
(InstrumentedTransport): Just make hooks a class attribute.
(InstrumentedTransport._get_FTP): Run hook directly in the for
loop.
(TransportHooks.run_hook, TransportHooks.uninstall_hook): Not
needed. The hooks should be cleaned up by the test itself.
(TestInit.setUp.cleanup): Resset to default hooks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
189
189
              c
190
190
              d/
191
191
                e
192
 
            b-c
193
192
            f
194
193
        """
195
194
        tree = self.make_branch_and_tree('tree')
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']
 
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']
198
197
        self.build_tree(['tree/' + p for p in paths])
199
198
        tree.set_root_id('TREE_ROOT')
200
199
        tree.add([p.rstrip('/') for p in paths], file_ids)
201
200
        tree.commit('initial', rev_id='rev-1')
202
201
        revision_id = 'rev-1'
203
202
        # a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
204
 
        t = self.get_transport('tree')
 
203
        t = self.get_transport().clone('tree')
205
204
        a_text = t.get_bytes('a')
206
205
        a_sha = osutils.sha_string(a_text)
207
206
        a_len = len(a_text)
215
214
        e_text = t.get_bytes('b/d/e')
216
215
        e_sha = osutils.sha_string(e_text)
217
216
        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)
221
217
        # f_packed_stat = dirstate.pack_stat(os.stat('tree/f'))
222
218
        f_text = t.get_bytes('f')
223
219
        f_sha = osutils.sha_string(f_text)
248
244
                      ('f', '', 0, False, null_stat),
249
245
                      ('f', e_sha, e_len, False, revision_id),
250
246
                     ]),
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
 
                     ]),
255
247
            'f':(('', 'f', 'f-id'), [
256
248
                  ('f', '', 0, False, null_stat),
257
249
                  ('f', f_sha, f_len, False, revision_id),
284
276
        tree, state, expected = self.create_basic_dirstate()
285
277
        # Now we will just remove and add every file so we get an extra entry
286
278
        # per entry. Unversion in reverse order so we handle subdirs
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'])
 
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'])
290
282
 
291
283
        # Update the expected dictionary.
292
 
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f']:
 
284
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
293
285
            orig = expected[path]
294
286
            path2 = path + '2'
295
287
            # This record was deleted in the current tree
352
344
            state.lock_read()
353
345
        return tree, state, expected
354
346
 
355
 
 
356
347
class TestTreeToDirState(TestCaseWithDirState):
357
348
 
358
349
    def test_empty_to_dirstate(self):
567
558
            self.assertEqual('', entry[1][0][1])
568
559
            # We should have a real entry.
569
560
            self.assertNotEqual((None, None), entry)
570
 
            # Make sure everything is old enough
571
 
            state._sha_cutoff_time()
572
 
            state._cutoff_time += 10
573
561
            sha1sum = state.update_entry(entry, 'a-file', os.lstat('a-file'))
574
562
            # We should have gotten a real sha1
575
563
            self.assertEqual('ecc5374e9ed82ad3ea3b4d452ea995a5fd3e70e3',
700
688
            # This will unlock it
701
689
            self.check_state_with_reopen(expected_result, state)
702
690
 
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
 
 
744
691
    def test_set_path_id_no_parents(self):
745
692
        """The id of a path can be changed trivally with no parents."""
746
693
        state = dirstate.DirState.initialize('dirstate')
1474
1421
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1475
1422
                         link_or_sha1)
1476
1423
 
1477
 
        # The dirblock entry should not cache the file's sha1
1478
 
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
 
1424
        # The dirblock entry should be updated with the new info
 
1425
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1479
1426
                         entry[1])
1480
1427
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1481
1428
                         state._dirblock_state)
1500
1447
                         link_or_sha1)
1501
1448
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1502
1449
                         state._dirblock_state)
1503
 
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1504
 
                         entry[1])
1505
1450
        state.save()
1506
1451
 
1507
1452
        # However, if we move the clock forward so the file is considered
1508
 
        # "stable", it should just cache the value.
1509
 
        state.adjust_time(+20)
 
1453
        # "stable", it should just returned the cached value.
 
1454
        state.adjust_time(20)
1510
1455
        link_or_sha1 = state.update_entry(entry, abspath='a',
1511
1456
                                          stat_value=stat_value)
1512
1457
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1513
1458
                         link_or_sha1)
1514
1459
        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1515
1460
                          ('sha1', 'a'), ('is_exec', mode, False),
1516
 
                          ('sha1', 'a'), ('is_exec', mode, False),
1517
1461
                         ], state._log)
1518
 
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1519
 
                         entry[1])
1520
1462
 
1521
 
        # Subsequent calls will just return the cached value
1522
 
        link_or_sha1 = state.update_entry(entry, abspath='a',
1523
 
                                          stat_value=stat_value)
 
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')
1524
1470
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1525
1471
                         link_or_sha1)
1526
 
        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1527
 
                          ('sha1', 'a'), ('is_exec', mode, False),
1528
 
                          ('sha1', 'a'), ('is_exec', mode, False),
 
1472
        stat_value = os.lstat('a')
 
1473
        self.assertEqual([('lstat', 'a'), ('sha1', 'a'),
 
1474
                          ('is_exec', stat_value.st_mode, False),
1529
1475
                         ], state._log)
1530
 
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1531
 
                         entry[1])
1532
1476
 
1533
1477
    def test_update_entry_symlink(self):
1534
1478
        """Update entry should read symlinks."""
1548
1492
                                          stat_value=stat_value)
1549
1493
        self.assertEqual('target', link_or_sha1)
1550
1494
        self.assertEqual([('read_link', 'a', '')], state._log)
1551
 
        # Dirblock is not updated (the link is too new)
1552
 
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
 
1495
        # Dirblock is updated
 
1496
        self.assertEqual([('l', link_or_sha1, 6, False, packed_stat)],
1553
1497
                         entry[1])
1554
1498
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1555
1499
                         state._dirblock_state)
1559
1503
                                          stat_value=stat_value)
1560
1504
        self.assertEqual('target', link_or_sha1)
1561
1505
        self.assertEqual([('read_link', 'a', ''),
1562
 
                          ('read_link', 'a', ''),
 
1506
                          ('read_link', 'a', 'target'),
1563
1507
                         ], state._log)
1564
 
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1565
 
                         entry[1])
1566
1508
        state.adjust_time(+20) # Skip into the future, all files look old
1567
1509
        link_or_sha1 = state.update_entry(entry, abspath='a',
1568
1510
                                          stat_value=stat_value)
1569
1511
        self.assertEqual('target', link_or_sha1)
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)
 
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)
1592
1518
 
1593
1519
    def test_update_entry_dir(self):
1594
1520
        state, entry = self.get_state_with_a()
1595
1521
        self.build_tree(['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)
 
1522
        self.assertIs(None, state.update_entry(entry, 'a'))
1626
1523
 
1627
1524
    def create_and_test_file(self, state, entry):
1628
1525
        """Create a file at 'a' and verify the state finds it.
1634
1531
        stat_value = os.lstat('a')
1635
1532
        packed_stat = dirstate.pack_stat(stat_value)
1636
1533
 
1637
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
 
1534
        link_or_sha1 = state.update_entry(entry, abspath='a')
1638
1535
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1639
1536
                         link_or_sha1)
1640
1537
        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1651
1548
        stat_value = os.lstat('a')
1652
1549
        packed_stat = dirstate.pack_stat(stat_value)
1653
1550
 
1654
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
 
1551
        link_or_sha1 = state.update_entry(entry, abspath='a')
1655
1552
        self.assertIs(None, link_or_sha1)
1656
1553
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1657
1554
 
1672
1569
        stat_value = os.lstat('a')
1673
1570
        packed_stat = dirstate.pack_stat(stat_value)
1674
1571
 
1675
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
 
1572
        link_or_sha1 = state.update_entry(entry, abspath='a')
1676
1573
        self.assertEqual('path/to/foo', link_or_sha1)
1677
1574
        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1678
1575
                         entry[1])
1679
1576
        return packed_stat
1680
1577
 
 
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
 
1681
1611
    def test_update_file_to_dir(self):
1682
1612
        """If a file changes to a directory we return None for the sha.
1683
1613
        We also update the inventory record.
1684
1614
        """
1685
1615
        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)
1688
1616
        self.create_and_test_file(state, entry)
1689
1617
        os.remove('a')
1690
1618
        self.create_and_test_dir(state, entry)
1695
1623
            # PlatformDeficiency / TestSkipped
1696
1624
            raise TestSkipped("No symlink support")
1697
1625
        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)
1700
1626
        self.create_and_test_file(state, entry)
1701
1627
        os.remove('a')
1702
1628
        self.create_and_test_symlink(state, entry)
1704
1630
    def test_update_dir_to_file(self):
1705
1631
        """Directory becoming a file updates the entry."""
1706
1632
        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)
1709
1633
        self.create_and_test_dir(state, entry)
1710
1634
        os.rmdir('a')
1711
1635
        self.create_and_test_file(state, entry)
1716
1640
            # PlatformDeficiency / TestSkipped
1717
1641
            raise TestSkipped("No symlink support")
1718
1642
        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)
1721
1643
        self.create_and_test_dir(state, entry)
1722
1644
        os.rmdir('a')
1723
1645
        self.create_and_test_symlink(state, entry)
1727
1649
        if not has_symlinks():
1728
1650
            raise TestSkipped("No symlink support")
1729
1651
        state, entry = self.get_state_with_a()
1730
 
        # The symlink and file info won't be cached unless old
1731
 
        state.adjust_time(+10)
1732
1652
        self.create_and_test_symlink(state, entry)
1733
1653
        os.remove('a')
1734
1654
        self.create_and_test_file(state, entry)
1738
1658
        if not has_symlinks():
1739
1659
            raise TestSkipped("No symlink support")
1740
1660
        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)
1743
1661
        self.create_and_test_symlink(state, entry)
1744
1662
        os.remove('a')
1745
1663
        self.create_and_test_dir(state, entry)
1759
1677
        packed_stat = dirstate.pack_stat(stat_value)
1760
1678
 
1761
1679
        state.adjust_time(-10) # Make sure everything is new
 
1680
        # Make sure it wants to kkkkkkkk
1762
1681
        state.update_entry(entry, abspath='a', stat_value=stat_value)
1763
1682
 
1764
1683
        # 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)
1770
1684
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1771
 
        state.update_entry(entry, abspath='a', stat_value=stat_value)
1772
1685
        self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
1773
1686
 
1774
1687
 
1827
1740
class TestBisect(TestCaseWithDirState):
1828
1741
    """Test the ability to bisect into the disk format."""
1829
1742
 
 
1743
 
1830
1744
    def assertBisect(self, expected_map, map_keys, state, paths):
1831
1745
        """Assert that bisecting for paths returns the right result.
1832
1746
 
1837
1751
                      (dir, name) tuples, and sorted according to how _bisect
1838
1752
                      requires.
1839
1753
        """
1840
 
        result = state._bisect(paths)
 
1754
        dir_names = sorted(osutils.split(p) for p in paths)
 
1755
        result = state._bisect(dir_names)
1841
1756
        # For now, results are just returned in whatever order we read them.
1842
1757
        # We could sort by (dir, name, file_id) or something like that, but in
1843
1758
        # the end it would still be fairly arbitrary, and we don't want the
1844
1759
        # extra overhead if we can avoid it. So sort everything to make sure
1845
1760
        # equality is true
1846
 
        assert len(map_keys) == len(paths)
 
1761
        assert len(map_keys) == len(dir_names)
1847
1762
        expected = {}
1848
 
        for path, keys in zip(paths, map_keys):
 
1763
        for dir_name, keys in zip(dir_names, map_keys):
1849
1764
            if keys is None:
1850
1765
                # This should not be present in the output
1851
1766
                continue
1852
 
            expected[path] = sorted(expected_map[k] for k in keys)
 
1767
            expected[dir_name] = sorted(expected_map[k] for k in keys)
1853
1768
 
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()
 
1769
        for dir_name in result:
 
1770
            result[dir_name].sort()
1858
1771
 
1859
1772
        self.assertEqual(expected, result)
1860
1773
 
1897
1810
            dir_name_id, trees_info = entry
1898
1811
            expected[dir_name_id] = trees_info
1899
1812
 
1900
 
        result = state._bisect_recursive(paths)
 
1813
        dir_names = sorted(osutils.split(p) for p in paths)
 
1814
        result = state._bisect_recursive(dir_names)
1901
1815
 
1902
1816
        self.assertEqual(expected, result)
1903
1817
 
1912
1826
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
1913
1827
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1914
1828
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1915
 
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
1916
1829
        self.assertBisect(expected, [['f']], state, ['f'])
1917
1830
 
1918
1831
    def test_bisect_multi(self):
1921
1834
        # Bisect should be capable of finding multiple entries at the same time
1922
1835
        self.assertBisect(expected, [['a'], ['b'], ['f']],
1923
1836
                          state, ['a', 'b', 'f'])
 
1837
        # ('', 'f') sorts before the others
1924
1838
        self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1925
 
                          state, ['f', 'b/d', 'b/d/e'])
1926
 
        self.assertBisect(expected, [['b'], ['b-c'], ['b/c']],
1927
 
                          state, ['b', 'b-c', 'b/c'])
 
1839
                          state, ['b/d', 'b/d/e', 'f'])
1928
1840
 
1929
1841
    def test_bisect_one_page(self):
1930
1842
        """Test bisect when there is only 1 page to read"""
1936
1848
        self.assertBisect(expected,[['b/c']], state, ['b/c'])
1937
1849
        self.assertBisect(expected,[['b/d']], state, ['b/d'])
1938
1850
        self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
1939
 
        self.assertBisect(expected,[['b-c']], state, ['b-c'])
1940
1851
        self.assertBisect(expected,[['f']], state, ['f'])
1941
1852
        self.assertBisect(expected,[['a'], ['b'], ['f']],
1942
1853
                          state, ['a', 'b', 'f'])
1943
 
        self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
 
1854
        # ('', 'f') sorts before the others
 
1855
        self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1944
1856
                          state, ['b/d', 'b/d/e', 'f'])
1945
 
        self.assertBisect(expected, [['b'], ['b/c'], ['b-c']],
1946
 
                          state, ['b', 'b/c', 'b-c'])
1947
1857
 
1948
1858
    def test_bisect_duplicate_paths(self):
1949
1859
        """When bisecting for a path, handle multiple entries."""
1957
1867
        self.assertBisect(expected, [['b/d', 'b/d2']], state, ['b/d'])
1958
1868
        self.assertBisect(expected, [['b/d/e', 'b/d/e2']],
1959
1869
                          state, ['b/d/e'])
1960
 
        self.assertBisect(expected, [['b-c', 'b-c2']], state, ['b-c'])
1961
1870
        self.assertBisect(expected, [['f', 'f2']], state, ['f'])
1962
1871
 
1963
1872
    def test_bisect_page_size_too_small(self):
1970
1879
        self.assertBisect(expected, [['b/c']], state, ['b/c'])
1971
1880
        self.assertBisect(expected, [['b/d']], state, ['b/d'])
1972
1881
        self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1973
 
        self.assertBisect(expected, [['b-c']], state, ['b-c'])
1974
1882
        self.assertBisect(expected, [['f']], state, ['f'])
1975
1883
 
1976
1884
    def test_bisect_missing(self):
1979
1887
        self.assertBisect(expected, [None], state, ['foo'])
1980
1888
        self.assertBisect(expected, [None], state, ['b/foo'])
1981
1889
        self.assertBisect(expected, [None], state, ['bar/foo'])
1982
 
        self.assertBisect(expected, [None], state, ['b-c/foo'])
1983
1890
 
1984
1891
        self.assertBisect(expected, [['a'], None, ['b/d']],
1985
1892
                          state, ['a', 'foo', 'b/d'])
2001
1908
    def test_bisect_dirblocks(self):
2002
1909
        tree, state, expected = self.create_duplicated_dirstate()
2003
1910
        self.assertBisectDirBlocks(expected,
2004
 
            [['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2']],
2005
 
            state, [''])
 
1911
            [['', 'a', 'a2', 'b', 'b2', 'f', 'f2']], state, [''])
2006
1912
        self.assertBisectDirBlocks(expected,
2007
1913
            [['b/c', 'b/c2', 'b/d', 'b/d2']], state, ['b'])
2008
1914
        self.assertBisectDirBlocks(expected,
2009
1915
            [['b/d/e', 'b/d/e2']], state, ['b/d'])
2010
1916
        self.assertBisectDirBlocks(expected,
2011
 
            [['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2'],
 
1917
            [['', 'a', 'a2', 'b', 'b2', 'f', 'f2'],
2012
1918
             ['b/c', 'b/c2', 'b/d', 'b/d2'],
2013
1919
             ['b/d/e', 'b/d/e2'],
2014
1920
            ], state, ['', 'b', 'b/d'])
2029
1935
        self.assertBisectRecursive(expected, ['a'], state, ['a'])
2030
1936
        self.assertBisectRecursive(expected, ['b/c'], state, ['b/c'])
2031
1937
        self.assertBisectRecursive(expected, ['b/d/e'], state, ['b/d/e'])
2032
 
        self.assertBisectRecursive(expected, ['b-c'], state, ['b-c'])
2033
1938
        self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
2034
1939
                                   state, ['b/d'])
2035
1940
        self.assertBisectRecursive(expected, ['b', 'b/c', 'b/d', 'b/d/e'],
2036
1941
                                   state, ['b'])
2037
 
        self.assertBisectRecursive(expected, ['', 'a', 'b', 'b-c', 'f', 'b/c',
 
1942
        self.assertBisectRecursive(expected, ['', 'a', 'b', 'f', 'b/c',
2038
1943
                                              'b/d', 'b/d/e'],
2039
1944
                                   state, [''])
2040
1945
 
2064
1969
                                   state, ['b'])
2065
1970
 
2066
1971
 
 
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
 
2067
2056
class TestDirstateValidation(TestCaseWithDirState):
2068
2057
 
2069
2058
    def test_validate_correct_dirstate(self):
2119
2108
            state._validate)
2120
2109
        self.assertContainsRe(str(e),
2121
2110
            'file a-id is absent in row')
2122