~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-03-21 04:49:05 UTC
  • mfrom: (2367.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070321044905-ded01a80ab49bdd9
Update NEWS to match bzr 0.15.

Show diffs side-by-side

added added

removed removed

Lines of Context:
102
102
         b/g      g-file
103
103
         b/h\xc3\xa5  h-\xc3\xa5-file  #This is u'\xe5' encoded into utf-8
104
104
 
105
 
        Notice that a/e is an empty directory.
106
 
 
107
 
        :return: The dirstate, still write-locked.
 
105
        # Notice that a/e is an empty directory.
108
106
        """
109
107
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
110
108
        null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
181
179
        finally:
182
180
            state.unlock()
183
181
 
184
 
    def create_basic_dirstate(self):
185
 
        """Create a dirstate with a few files and directories.
186
 
 
187
 
            a
188
 
            b/
189
 
              c
190
 
              d/
191
 
                e
192
 
            f
193
 
        """
194
 
        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']
197
 
        self.build_tree(['tree/' + p for p in paths])
198
 
        tree.set_root_id('TREE_ROOT')
199
 
        tree.add([p.rstrip('/') for p in paths], file_ids)
200
 
        tree.commit('initial', rev_id='rev-1')
201
 
        revision_id = 'rev-1'
202
 
        # a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
203
 
        t = self.get_transport().clone('tree')
204
 
        a_text = t.get_bytes('a')
205
 
        a_sha = osutils.sha_string(a_text)
206
 
        a_len = len(a_text)
207
 
        # b_packed_stat = dirstate.pack_stat(os.stat('tree/b'))
208
 
        # c_packed_stat = dirstate.pack_stat(os.stat('tree/b/c'))
209
 
        c_text = t.get_bytes('b/c')
210
 
        c_sha = osutils.sha_string(c_text)
211
 
        c_len = len(c_text)
212
 
        # d_packed_stat = dirstate.pack_stat(os.stat('tree/b/d'))
213
 
        # e_packed_stat = dirstate.pack_stat(os.stat('tree/b/d/e'))
214
 
        e_text = t.get_bytes('b/d/e')
215
 
        e_sha = osutils.sha_string(e_text)
216
 
        e_len = len(e_text)
217
 
        # f_packed_stat = dirstate.pack_stat(os.stat('tree/f'))
218
 
        f_text = t.get_bytes('f')
219
 
        f_sha = osutils.sha_string(f_text)
220
 
        f_len = len(f_text)
221
 
        null_stat = dirstate.DirState.NULLSTAT
222
 
        expected = {
223
 
            '':(('', '', 'TREE_ROOT'), [
224
 
                  ('d', '', 0, False, null_stat),
225
 
                  ('d', '', 0, False, revision_id),
226
 
                ]),
227
 
            'a':(('', 'a', 'a-id'), [
228
 
                   ('f', '', 0, False, null_stat),
229
 
                   ('f', a_sha, a_len, False, revision_id),
230
 
                 ]),
231
 
            'b':(('', 'b', 'b-id'), [
232
 
                  ('d', '', 0, False, null_stat),
233
 
                  ('d', '', 0, False, revision_id),
234
 
                 ]),
235
 
            'b/c':(('b', 'c', 'c-id'), [
236
 
                    ('f', '', 0, False, null_stat),
237
 
                    ('f', c_sha, c_len, False, revision_id),
238
 
                   ]),
239
 
            'b/d':(('b', 'd', 'd-id'), [
240
 
                    ('d', '', 0, False, null_stat),
241
 
                    ('d', '', 0, False, revision_id),
242
 
                   ]),
243
 
            'b/d/e':(('b/d', 'e', 'e-id'), [
244
 
                      ('f', '', 0, False, null_stat),
245
 
                      ('f', e_sha, e_len, False, revision_id),
246
 
                     ]),
247
 
            'f':(('', 'f', 'f-id'), [
248
 
                  ('f', '', 0, False, null_stat),
249
 
                  ('f', f_sha, f_len, False, revision_id),
250
 
                 ]),
251
 
        }
252
 
        state = dirstate.DirState.from_tree(tree, 'dirstate')
253
 
        try:
254
 
            state.save()
255
 
        finally:
256
 
            state.unlock()
257
 
        # Use a different object, to make sure nothing is pre-cached in memory.
258
 
        state = dirstate.DirState.on_file('dirstate')
259
 
        state.lock_read()
260
 
        self.addCleanup(state.unlock)
261
 
        self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
262
 
                         state._dirblock_state)
263
 
        # This is code is only really tested if we actually have to make more
264
 
        # than one read, so set the page size to something smaller.
265
 
        # We want it to contain about 2.2 records, so that we have a couple
266
 
        # records that we can read per attempt
267
 
        state._bisect_page_size = 200
268
 
        return tree, state, expected
269
 
 
270
 
    def create_duplicated_dirstate(self):
271
 
        """Create a dirstate with a deleted and added entries.
272
 
 
273
 
        This grabs a basic_dirstate, and then removes and re adds every entry
274
 
        with a new file id.
275
 
        """
276
 
        tree, state, expected = self.create_basic_dirstate()
277
 
        # Now we will just remove and add every file so we get an extra entry
278
 
        # 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'])
282
 
 
283
 
        # Update the expected dictionary.
284
 
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
285
 
            orig = expected[path]
286
 
            path2 = path + '2'
287
 
            # This record was deleted in the current tree
288
 
            expected[path] = (orig[0], [dirstate.DirState.NULL_PARENT_DETAILS,
289
 
                                        orig[1][1]])
290
 
            new_key = (orig[0][0], orig[0][1], orig[0][2]+'2')
291
 
            # And didn't exist in the basis tree
292
 
            expected[path2] = (new_key, [orig[1][0],
293
 
                                         dirstate.DirState.NULL_PARENT_DETAILS])
294
 
 
295
 
        # We will replace the 'dirstate' file underneath 'state', but that is
296
 
        # okay as lock as we unlock 'state' first.
297
 
        state.unlock()
298
 
        try:
299
 
            new_state = dirstate.DirState.from_tree(tree, 'dirstate')
300
 
            try:
301
 
                new_state.save()
302
 
            finally:
303
 
                new_state.unlock()
304
 
        finally:
305
 
            # But we need to leave state in a read-lock because we already have
306
 
            # a cleanup scheduled
307
 
            state.lock_read()
308
 
        return tree, state, expected
309
 
 
310
 
    def create_renamed_dirstate(self):
311
 
        """Create a dirstate with a few internal renames.
312
 
 
313
 
        This takes the basic dirstate, and moves the paths around.
314
 
        """
315
 
        tree, state, expected = self.create_basic_dirstate()
316
 
        # Rename a file
317
 
        tree.rename_one('a', 'b/g')
318
 
        # And a directory
319
 
        tree.rename_one('b/d', 'h')
320
 
 
321
 
        old_a = expected['a']
322
 
        expected['a'] = (old_a[0], [('r', 'b/g', 0, False, ''), old_a[1][1]])
323
 
        expected['b/g'] = (('b', 'g', 'a-id'), [old_a[1][0],
324
 
                                                ('r', 'a', 0, False, '')])
325
 
        old_d = expected['b/d']
326
 
        expected['b/d'] = (old_d[0], [('r', 'h', 0, False, ''), old_d[1][1]])
327
 
        expected['h'] = (('', 'h', 'd-id'), [old_d[1][0],
328
 
                                             ('r', 'b/d', 0, False, '')])
329
 
 
330
 
        old_e = expected['b/d/e']
331
 
        expected['b/d/e'] = (old_e[0], [('r', 'h/e', 0, False, ''),
332
 
                             old_e[1][1]])
333
 
        expected['h/e'] = (('h', 'e', 'e-id'), [old_e[1][0],
334
 
                                                ('r', 'b/d/e', 0, False, '')])
335
 
 
336
 
        state.unlock()
337
 
        try:
338
 
            new_state = dirstate.DirState.from_tree(tree, 'dirstate')
339
 
            try:
340
 
                new_state.save()
341
 
            finally:
342
 
                new_state.unlock()
343
 
        finally:
344
 
            state.lock_read()
345
 
        return tree, state, expected
346
182
 
347
183
class TestTreeToDirState(TestCaseWithDirState):
348
184
 
370
206
             ])])
371
207
        state = dirstate.DirState.from_tree(tree, 'dirstate')
372
208
        self.check_state_with_reopen(expected_result, state)
373
 
        state.lock_read()
374
 
        try:
375
 
            state._validate()
376
 
        finally:
377
 
            state.unlock()
 
209
        state._validate()
378
210
 
379
211
    def test_2_parents_empty_to_dirstate(self):
380
212
        # create a parent by doing a commit
391
223
             ])])
392
224
        state = dirstate.DirState.from_tree(tree, 'dirstate')
393
225
        self.check_state_with_reopen(expected_result, state)
394
 
        state.lock_read()
395
 
        try:
396
 
            state._validate()
397
 
        finally:
398
 
            state.unlock()
 
226
        state._validate()
399
227
 
400
228
    def test_empty_unknowns_are_ignored_to_dirstate(self):
401
229
        """We should be able to create a dirstate for an empty tree."""
705
533
        finally:
706
534
            state.unlock()
707
535
        state = dirstate.DirState.on_file('dirstate')
 
536
        state._validate()
708
537
        state.lock_read()
709
538
        try:
710
 
            state._validate()
711
539
            self.assertEqual(expected_rows, list(state._iter_entries()))
712
540
        finally:
713
541
            state.unlock()
1735
1563
        self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1736
1564
 
1737
1565
 
1738
 
class TestBisect(TestCaseWithDirState):
 
1566
class TestBisect(TestCaseWithTransport):
1739
1567
    """Test the ability to bisect into the disk format."""
1740
1568
 
 
1569
    def create_basic_dirstate(self):
 
1570
        """Create a dirstate with a few files and directories.
 
1571
 
 
1572
            a
 
1573
            b/
 
1574
              c
 
1575
              d/
 
1576
                e
 
1577
            f
 
1578
        """
 
1579
        tree = self.make_branch_and_tree('tree')
 
1580
        paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'f']
 
1581
        file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'f-id']
 
1582
        self.build_tree(['tree/' + p for p in paths])
 
1583
        tree.set_root_id('TREE_ROOT')
 
1584
        tree.add([p.rstrip('/') for p in paths], file_ids)
 
1585
        tree.commit('initial', rev_id='rev-1')
 
1586
        revision_id = 'rev-1'
 
1587
        # a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
 
1588
        t = self.get_transport().clone('tree')
 
1589
        a_text = t.get_bytes('a')
 
1590
        a_sha = osutils.sha_string(a_text)
 
1591
        a_len = len(a_text)
 
1592
        # b_packed_stat = dirstate.pack_stat(os.stat('tree/b'))
 
1593
        # c_packed_stat = dirstate.pack_stat(os.stat('tree/b/c'))
 
1594
        c_text = t.get_bytes('b/c')
 
1595
        c_sha = osutils.sha_string(c_text)
 
1596
        c_len = len(c_text)
 
1597
        # d_packed_stat = dirstate.pack_stat(os.stat('tree/b/d'))
 
1598
        # e_packed_stat = dirstate.pack_stat(os.stat('tree/b/d/e'))
 
1599
        e_text = t.get_bytes('b/d/e')
 
1600
        e_sha = osutils.sha_string(e_text)
 
1601
        e_len = len(e_text)
 
1602
        # f_packed_stat = dirstate.pack_stat(os.stat('tree/f'))
 
1603
        f_text = t.get_bytes('f')
 
1604
        f_sha = osutils.sha_string(f_text)
 
1605
        f_len = len(f_text)
 
1606
        null_stat = dirstate.DirState.NULLSTAT
 
1607
        expected = {
 
1608
            '':(('', '', 'TREE_ROOT'), [
 
1609
                  ('d', '', 0, False, null_stat),
 
1610
                  ('d', '', 0, False, revision_id),
 
1611
                ]),
 
1612
            'a':(('', 'a', 'a-id'), [
 
1613
                   ('f', '', 0, False, null_stat),
 
1614
                   ('f', a_sha, a_len, False, revision_id),
 
1615
                 ]),
 
1616
            'b':(('', 'b', 'b-id'), [
 
1617
                  ('d', '', 0, False, null_stat),
 
1618
                  ('d', '', 0, False, revision_id),
 
1619
                 ]),
 
1620
            'b/c':(('b', 'c', 'c-id'), [
 
1621
                    ('f', '', 0, False, null_stat),
 
1622
                    ('f', c_sha, c_len, False, revision_id),
 
1623
                   ]),
 
1624
            'b/d':(('b', 'd', 'd-id'), [
 
1625
                    ('d', '', 0, False, null_stat),
 
1626
                    ('d', '', 0, False, revision_id),
 
1627
                   ]),
 
1628
            'b/d/e':(('b/d', 'e', 'e-id'), [
 
1629
                      ('f', '', 0, False, null_stat),
 
1630
                      ('f', e_sha, e_len, False, revision_id),
 
1631
                     ]),
 
1632
            'f':(('', 'f', 'f-id'), [
 
1633
                  ('f', '', 0, False, null_stat),
 
1634
                  ('f', f_sha, f_len, False, revision_id),
 
1635
                 ]),
 
1636
        }
 
1637
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
1638
        try:
 
1639
            state.save()
 
1640
        finally:
 
1641
            state.unlock()
 
1642
        # Use a different object, to make sure nothing is pre-cached in memory.
 
1643
        state = dirstate.DirState.on_file('dirstate')
 
1644
        state.lock_read()
 
1645
        self.addCleanup(state.unlock)
 
1646
        self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
 
1647
                         state._dirblock_state)
 
1648
        # This is code is only really tested if we actually have to make more
 
1649
        # than one read, so set the page size to something smaller.
 
1650
        # We want it to contain about 2.2 records, so that we have a couple
 
1651
        # records that we can read per attempt
 
1652
        state._bisect_page_size = 200
 
1653
        return tree, state, expected
 
1654
 
 
1655
    def create_duplicated_dirstate(self):
 
1656
        """Create a dirstate with a deleted and added entries.
 
1657
 
 
1658
        This grabs a basic_dirstate, and then removes and re adds every entry
 
1659
        with a new file id.
 
1660
        """
 
1661
        tree, state, expected = self.create_basic_dirstate()
 
1662
        # Now we will just remove and add every file so we get an extra entry
 
1663
        # per entry. Unversion in reverse order so we handle subdirs
 
1664
        tree.unversion(['f-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
 
1665
        tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f'],
 
1666
                 ['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'f-id2'])
 
1667
 
 
1668
        # Update the expected dictionary.
 
1669
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
 
1670
            orig = expected[path]
 
1671
            path2 = path + '2'
 
1672
            # This record was deleted in the current tree
 
1673
            expected[path] = (orig[0], [dirstate.DirState.NULL_PARENT_DETAILS,
 
1674
                                        orig[1][1]])
 
1675
            new_key = (orig[0][0], orig[0][1], orig[0][2]+'2')
 
1676
            # And didn't exist in the basis tree
 
1677
            expected[path2] = (new_key, [orig[1][0],
 
1678
                                         dirstate.DirState.NULL_PARENT_DETAILS])
 
1679
 
 
1680
        # We will replace the 'dirstate' file underneath 'state', but that is
 
1681
        # okay as lock as we unlock 'state' first.
 
1682
        state.unlock()
 
1683
        try:
 
1684
            new_state = dirstate.DirState.from_tree(tree, 'dirstate')
 
1685
            try:
 
1686
                new_state.save()
 
1687
            finally:
 
1688
                new_state.unlock()
 
1689
        finally:
 
1690
            # But we need to leave state in a read-lock because we already have
 
1691
            # a cleanup scheduled
 
1692
            state.lock_read()
 
1693
        return tree, state, expected
 
1694
 
 
1695
    def create_renamed_dirstate(self):
 
1696
        """Create a dirstate with a few internal renames.
 
1697
 
 
1698
        This takes the basic dirstate, and moves the paths around.
 
1699
        """
 
1700
        tree, state, expected = self.create_basic_dirstate()
 
1701
        # Rename a file
 
1702
        tree.rename_one('a', 'b/g')
 
1703
        # And a directory
 
1704
        tree.rename_one('b/d', 'h')
 
1705
 
 
1706
        old_a = expected['a']
 
1707
        expected['a'] = (old_a[0], [('r', 'b/g', 0, False, ''), old_a[1][1]])
 
1708
        expected['b/g'] = (('b', 'g', 'a-id'), [old_a[1][0],
 
1709
                                                ('r', 'a', 0, False, '')])
 
1710
        old_d = expected['b/d']
 
1711
        expected['b/d'] = (old_d[0], [('r', 'h', 0, False, ''), old_d[1][1]])
 
1712
        expected['h'] = (('', 'h', 'd-id'), [old_d[1][0],
 
1713
                                             ('r', 'b/d', 0, False, '')])
 
1714
 
 
1715
        old_e = expected['b/d/e']
 
1716
        expected['b/d/e'] = (old_e[0], [('r', 'h/e', 0, False, ''),
 
1717
                             old_e[1][1]])
 
1718
        expected['h/e'] = (('h', 'e', 'e-id'), [old_e[1][0],
 
1719
                                                ('r', 'b/d/e', 0, False, '')])
 
1720
 
 
1721
        state.unlock()
 
1722
        try:
 
1723
            new_state = dirstate.DirState.from_tree(tree, 'dirstate')
 
1724
            try:
 
1725
                new_state.save()
 
1726
            finally:
 
1727
                new_state.unlock()
 
1728
        finally:
 
1729
            state.lock_read()
 
1730
        return tree, state, expected
1741
1731
 
1742
1732
    def assertBisect(self, expected_map, map_keys, state, paths):
1743
1733
        """Assert that bisecting for paths returns the right result.
2050
2040
        for path in paths:
2051
2041
            self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
2052
2042
 
2053
 
 
2054
 
class TestDirstateValidation(TestCaseWithDirState):
2055
 
 
2056
 
    def test_validate_correct_dirstate(self):
2057
 
        state = self.create_complex_dirstate()
2058
 
        state._validate()
2059
 
        state.unlock()
2060
 
        # and make sure we can also validate with a read lock
2061
 
        state.lock_read()
2062
 
        try:
2063
 
            state._validate()
2064
 
        finally:
2065
 
            state.unlock()
2066
 
 
2067
 
    def test_dirblock_not_sorted(self):
2068
 
        tree, state, expected = self.create_renamed_dirstate()
2069
 
        state._read_dirblocks_if_needed()
2070
 
        last_dirblock = state._dirblocks[-1]
2071
 
        # we're appending to the dirblock, but this name comes before some of
2072
 
        # the existing names; that's wrong
2073
 
        last_dirblock[1].append(
2074
 
            (('h', 'aaaa', 'a-id'),
2075
 
             [('a', '', 0, False, ''),
2076
 
              ('a', '', 0, False, '')]))
2077
 
        e = self.assertRaises(AssertionError,
2078
 
            state._validate)
2079
 
        self.assertContainsRe(str(e), 'not sorted')
2080
 
 
2081
 
    def test_dirblock_name_mismatch(self):
2082
 
        tree, state, expected = self.create_renamed_dirstate()
2083
 
        state._read_dirblocks_if_needed()
2084
 
        last_dirblock = state._dirblocks[-1]
2085
 
        # add an entry with the wrong directory name
2086
 
        last_dirblock[1].append(
2087
 
            (('', 'z', 'a-id'),
2088
 
             [('a', '', 0, False, ''),
2089
 
              ('a', '', 0, False, '')]))
2090
 
        e = self.assertRaises(AssertionError,
2091
 
            state._validate)
2092
 
        self.assertContainsRe(str(e),
2093
 
            "doesn't match directory name")
2094
 
 
2095
 
    def test_dirblock_missing_rename(self):
2096
 
        tree, state, expected = self.create_renamed_dirstate()
2097
 
        state._read_dirblocks_if_needed()
2098
 
        last_dirblock = state._dirblocks[-1]
2099
 
        # make another entry for a-id, without a correct 'r' pointer to
2100
 
        # the real occurrence in the working tree
2101
 
        last_dirblock[1].append(
2102
 
            (('h', 'z', 'a-id'),
2103
 
             [('a', '', 0, False, ''),
2104
 
              ('a', '', 0, False, '')]))
2105
 
        e = self.assertRaises(AssertionError,
2106
 
            state._validate)
2107
 
        self.assertContainsRe(str(e),
2108
 
            'file a-id is absent in row')