/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

Split out working tree format 4 to its own file, create stub dirstate revision object, start working on dirstate.set_parent_trees - a key failure point.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 by Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests of the dirstate functionality being built for WorkingTreeFormat4."""
 
18
 
 
19
import os
 
20
 
 
21
from bzrlib import dirstate
 
22
from bzrlib.tests import TestCaseWithTransport
 
23
 
 
24
 
 
25
# TODO:
 
26
# test 0 parents, 1 parent, 4 parents.
 
27
# test unicode parents, non unicode parents
 
28
# test all change permutations in one and two parents.
 
29
# i.e. file in parent 1, dir in parent 2, symlink in tree.
 
30
# test that renames in the tree result in correct parent paths 
 
31
# Test get state from a file, then asking for lines.
 
32
# write a smaller state, and check the file has been truncated.
 
33
# add a entry when its in state deleted
 
34
# revision attribute for root entries.
 
35
# test that utf8 strings are preserved in _row_to_line
 
36
# test parent manipulation 
 
37
# test parents that are null in save : i.e. no record in the parent tree for this.
 
38
# todo: _set_data records ghost parents.
 
39
 
 
40
class TestTreeToDirstate(TestCaseWithTransport):
 
41
 
 
42
    def test_empty_to_dirstate(self):
 
43
        """We should be able to create a dirstate for an empty tree."""
 
44
        # There are no files on disk and no parents
 
45
        tree = self.make_branch_and_tree('tree')
 
46
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
47
        def check_state():
 
48
            # an inner function because there is no parameterisation at this point
 
49
            # if we make it reusable that would be a good thing.
 
50
            self.assertEqual([],  state.get_parent_ids())
 
51
            # there should be no ghosts in this tree.
 
52
            self.assertEqual([], state.get_ghosts())
 
53
            # there should be one fileid in this tree - the root of the tree.
 
54
            root_stat_pack = dirstate.pack_stat(os.stat(tree.basedir))
 
55
            self.assertEqual(
 
56
                [(['', '', 'directory', tree.inventory.root.file_id, 0, root_stat_pack, ''], [])],
 
57
                list(state._iter_rows()))
 
58
        check_state()
 
59
        state = dirstate.DirState.on_file('dirstate')
 
60
        check_state()
 
61
 
 
62
    def test_1_parents_empty_to_dirstate(self):
 
63
        # create a parent by doing a commit
 
64
        tree = self.make_branch_and_tree('tree')
 
65
        rev_id = tree.commit('first post')
 
66
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
67
        # we want to be able to get the lines of the dirstate that we will
 
68
        # write to disk.
 
69
        lines = state.get_lines()
 
70
        # we now have parent revisions, and all the files in the tree were
 
71
        # last modified in the parent.
 
72
        expected_lines_re = (
 
73
            '#bazaar dirstate flat format 1\n'
 
74
            'adler32: [0-9-][0-9]*\n'
 
75
            'num_entries: 1\n'
 
76
            '1\x00.*\x00\n\x00'
 
77
            '0\x00\n\x00'
 
78
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00\n'
 
79
            '\x00$') % rev_id.encode('utf8')
 
80
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
81
 
 
82
    def test_2_parents_empty_to_dirstate(self):
 
83
        # create a parent by doing a commit
 
84
        tree = self.make_branch_and_tree('tree')
 
85
        rev_id = tree.commit('first post')
 
86
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
87
        rev_id2 = tree2.commit('second post', allow_pointless=True)
 
88
        tree.merge_from_branch(tree2.branch)
 
89
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
90
        # we want to be able to get the lines of the dirstate that we will
 
91
        # write to disk.
 
92
        lines = state.get_lines()
 
93
        # we now have parent revisions, and all the files in the tree were
 
94
        # last modified in the parent.
 
95
        expected_lines_re = (
 
96
            '#bazaar dirstate flat format 1\n'
 
97
            'adler32: [0-9-][0-9]*\n'
 
98
            'num_entries: 1\n'
 
99
            '2\x00.*\x00.*\x00\n\x00'
 
100
            '0\x00\n\x00'
 
101
            '\x00\x00d\x00TREE_ROOT\x000\x00[0-9a-zA-Z+/]{32}\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00\n'
 
102
            '\x00$') % (rev_id.encode('utf8'), rev_id2.encode('utf8'))
 
103
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
104
        
 
105
    def test_empty_unknowns_are_ignored_to_dirstate(self):
 
106
        """We should be able to create a dirstate for an empty tree."""
 
107
        # There are no files on disk and no parents
 
108
        tree = self.make_branch_and_tree('tree')
 
109
        self.build_tree(['tree/unknown'])
 
110
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
111
        # we want to be able to get the lines of the dirstate that we will
 
112
        # write to disk.
 
113
        lines = state.get_lines()
 
114
        expected_lines_re = (
 
115
            '#bazaar dirstate flat format 1\n'
 
116
            'adler32: [0-9-][0-9]*\n'
 
117
            'num_entries: 1\n'
 
118
            '0\x00\n\x00'
 
119
            '0\x00\n\x00'
 
120
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00\n'
 
121
            '\x00$')
 
122
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
123
        
 
124
    def get_tree_with_a_file(self):
 
125
        tree = self.make_branch_and_tree('tree')
 
126
        self.build_tree(['tree/a file'])
 
127
        tree.add('a file', 'a file id')
 
128
        return tree
 
129
 
 
130
    def test_non_empty_no_parents_to_dirstate(self):
 
131
        """We should be able to create a dirstate for an empty tree."""
 
132
        # There are files on disk and no parents
 
133
        tree = self.get_tree_with_a_file()
 
134
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
135
        # we want to be able to get the lines of the dirstate that we will
 
136
        # write to disk.
 
137
        lines = state.get_lines()
 
138
        expected_lines_re = (
 
139
            '#bazaar dirstate flat format 1\n'
 
140
            'adler32: [0-9-][0-9]*\n'
 
141
            'num_entries: 2\n'
 
142
            '0\x00\n\x00'
 
143
            '0\x00\n\x00'
 
144
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00\n'
 
145
            '\x00\x00a file\x00f\x00a file id\x0024\x00[0-9a-zA-Z+/]{32}\x00c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8\x00'
 
146
            '\n\x00$')
 
147
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
148
 
 
149
    def test_1_parents_not_empty_to_dirstate(self):
 
150
        # create a parent by doing a commit
 
151
        tree = self.get_tree_with_a_file()
 
152
        rev_id = tree.commit('first post')
 
153
        # change the current content to be different this will alter stat, sha
 
154
        # and length:
 
155
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
156
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
157
        # we want to be able to get the lines of the dirstate that we will
 
158
        # write to disk.
 
159
        lines = state.get_lines()
 
160
        # we now have parent revisions, and all the files in the tree were
 
161
        # last modified in the parent.
 
162
        expected_lines_re = (
 
163
            '#bazaar dirstate flat format 1\n'
 
164
            'adler32: [0-9-][0-9]*\n'
 
165
            'num_entries: 2\n'
 
166
            '1\x00.*\x00\n\x00'
 
167
            '0\x00\n\x00'
 
168
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00\n'
 
169
            '\x00\x00a file\x00f\x00a file id\x0012\x00[0-9a-zA-Z+/]{32}\x008b787bd9293c8b962c7a637a9fdbf627fe68610e\x00%s\x00f\x00\x00a file\x0024\x00n\x00c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8\x00\n'
 
170
            '\x00$')  % (rev_id.encode('utf8'), rev_id.encode('utf8'))
 
171
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
172
 
 
173
    def test_2_parents_not_empty_to_dirstate(self):
 
174
        # create a parent by doing a commit
 
175
        tree = self.get_tree_with_a_file()
 
176
        rev_id = tree.commit('first post')
 
177
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
178
        # change the current content to be different this will alter stat, sha
 
179
        # and length:
 
180
        self.build_tree_contents([('tree2/a file', 'merge content\n')])
 
181
        rev_id2 = tree2.commit('second post')
 
182
        tree.merge_from_branch(tree2.branch)
 
183
        # change the current content to be different this will alter stat, sha
 
184
        # and length again, giving us three distinct values:
 
185
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
186
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
187
        # we want to be able to get the lines of the dirstate that we will
 
188
        # write to disk.
 
189
        lines = state.get_lines()
 
190
        # we now have parent revisions, and all the files in the tree were
 
191
        # last modified in the parent.
 
192
        expected_lines_re = (
 
193
            '#bazaar dirstate flat format 1\n'
 
194
            'adler32: [0-9-][0-9]*\n'
 
195
            'num_entries: 2\n'
 
196
            '2\x00.*\x00.*\x00\n\x00'
 
197
            '0\x00\n\x00'
 
198
            '\x00\x00d\x00TREE_ROOT\x000\x00[0-9a-zA-Z+/]{32}\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00\n\x00'
 
199
            '\x00a file\x00f\x00a file id\x0012\x00[0-9a-zA-Z+/]{32}\x008b787bd9293c8b962c7a637a9fdbf627fe68610e\x00%s\x00f\x00\x00a file\x0024\x00n\x00c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8\x00%s\x00f\x00\x00a file\x0014\x00n\x00314d796174c9412647c3ce07dfb5d36a94e72958\x00\n\x00$'
 
200
            % (rev_id.encode('utf8'), rev_id2.encode('utf8'),
 
201
               rev_id.encode('utf8'), rev_id2.encode('utf8')))
 
202
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
203
 
 
204
 
 
205
class TestDirStateOnFile(TestCaseWithTransport):
 
206
 
 
207
    def test_construct_with_path(self):
 
208
        tree = self.make_branch_and_tree('tree')
 
209
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
210
        # we want to be able to get the lines of the dirstate that we will
 
211
        # write to disk.
 
212
        lines = state.get_lines()
 
213
        self.build_tree_contents([('dirstate', ''.join(lines))])
 
214
        # get a state object
 
215
        state = dirstate.DirState.on_file('dirstate')
 
216
        # ask it for a parents list
 
217
        self.assertEqual([], state.get_parent_ids())
 
218
        # doing a save should work here as there have been no changes.
 
219
        state.save()
 
220
 
 
221
class TestDirStateInitialize(TestCaseWithTransport):
 
222
 
 
223
    def test_initialize(self):
 
224
        state = dirstate.DirState.initialize('dirstate')
 
225
        self.assertIsInstance(state, dirstate.DirState)
 
226
        self.assertFileEqual(
 
227
            '#bazaar dirstate flat format 1\n'
 
228
            'adler32: -455929114\n'
 
229
            'num_entries: 1\n'
 
230
            '0\x00\n\x00'
 
231
            '0\x00\n\x00'
 
232
            # after the 0 parent count, there is the \x00\n\x00 line delim
 
233
            # then '' for dir, '' for basame, and then 'd' for directory.
 
234
            # then the root value, 0 size, our constant xxxx packed stat, and 
 
235
            # an empty sha value. Finally a new \x00\n\x00 delimiter
 
236
            '\x00\x00d\x00TREE_ROOT\x000\x00xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\x00\x00\n'
 
237
            '\x00',
 
238
            'dirstate')
 
239
 
 
240
 
 
241
class TestDirstateManipulations(TestCaseWithTransport):
 
242
 
 
243
    def test_add_ghost_tree(self):
 
244
        state = dirstate.DirState.initialize('dirstate')
 
245
        state.add_parent_tree('a-ghost', None)
 
246
        # now the parent list should be changed:
 
247
        self.assertEqual(['a-ghost'], state.get_parent_ids())
 
248
        self.assertEqual(['a-ghost'], state.get_ghosts())
 
249
        # save the state and reopen to check its persistent
 
250
        state.save()
 
251
        state = dirstate.DirState.on_file('dirstate')
 
252
        self.assertEqual(['a-ghost'], state.get_parent_ids())
 
253
        self.assertEqual(['a-ghost'], state.get_ghosts())
 
254
 
 
255
    def test_set_parent_trees_no_content(self):
 
256
        # set_parent_trees is a slow but important api to support.
 
257
        state = dirstate.DirState.initialize('dirstate')
 
258
        tree1 = self.make_branch_and_memory_tree('tree1')
 
259
        tree1.lock_write()
 
260
        tree1.add('')
 
261
        revid1 = tree1.commit('foo')
 
262
        tree1.unlock()
 
263
        tree2 = self.make_branch_and_memory_tree('tree2')
 
264
        tree2.lock_write()
 
265
        tree2.add('')
 
266
        revid2 = tree2.commit('foo')
 
267
        tree2.unlock()
 
268
        state.set_parent_trees(
 
269
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
270
             (revid2, tree2.branch.repository.revision_tree(revid2)),
 
271
             ('ghost-rev', None)),
 
272
            ['ghost-rev'])
 
273
        # check we can reopen and use the dirstate after setting parent trees.
 
274
        state.save()
 
275
        state = dirstate.DirState.on_file('dirstate')
 
276
        self.assertEqual([revid1, revid2, 'ghost-rev'],  state.get_parent_ids())
 
277
        # iterating the entire state ensures that the state is parsable.
 
278
        list(state._iter_rows())
 
279
        # be sure that it sets not appends - change it
 
280
        state.set_parent_trees(
 
281
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
282
             ('ghost-rev', None)),
 
283
            ['ghost-rev'])
 
284
        # and now put it back.
 
285
        state.set_parent_trees(
 
286
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
287
             (revid2, tree2.branch.repository.revision_tree(revid2)),
 
288
             ('ghost-rev', tree2.branch.repository.revision_tree(None))),
 
289
            ['ghost-rev'])
 
290
        self.assertEqual([revid1, revid2, 'ghost-rev'],  state.get_parent_ids())
 
291
        # the ghost should be recorded as such by set_parent_trees.
 
292
        self.assertEqual(['ghost-rev'], state.get_ghosts())
 
293
        self.assertEqual(
 
294
            [(('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), [])],
 
295
            list(state._iter_rows()))
 
296
 
 
297
    ### add a path via _set_data - so we dont need delta work, just
 
298
    # raw data in, and ensure that it comes out via get_lines happily.
 
299
 
 
300
    def test_add_path_to_root_no_parents_all_data(self):
 
301
        # The most trivial addition of a path is when there are no parents and
 
302
        # its in the root and all data about the file is supplied
 
303
        state = dirstate.DirState.initialize('dirstate')
 
304
        self.build_tree(['a file'])
 
305
        stat = os.lstat('a file')
 
306
        # the 1*20 is the sha1 pretend value.
 
307
        state.add('a file', 'a file id', 'file', stat, '1'*20)
 
308
 
 
309
 
 
310
class TestGetLines(TestCaseWithTransport):
 
311
 
 
312
    def test_adding_ghost_tree_sets_ghosts_line(self):
 
313
        state = dirstate.DirState.initialize('dirstate')
 
314
        state.add_parent_tree('a-ghost', None)
 
315
        self.assertEqual(['#bazaar dirstate flat format 1\n',
 
316
            'adler32: 1202264142\n',
 
317
            'num_entries: 1\n',
 
318
            '1\x00a-ghost\x00\n\x00'
 
319
            '1\x00a-ghost\x00\n\x00'
 
320
            '\x00\x00d\x00TREE_ROOT\x000\x00'
 
321
            'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\x00\x00\n\x00'],
 
322
            state.get_lines())
 
323
 
 
324
    def test_adding_tree_changes_lines(self):
 
325
        state = dirstate.DirState.initialize('dirstate')
 
326
        lines = list(state.get_lines())
 
327
        state.add_parent_tree('a-ghost', None)
 
328
        self.assertNotEqual(lines, state.get_lines())
 
329
 
 
330
    def test_get_line_with_2_rows(self):
 
331
        state = dirstate.DirState.initialize('dirstate')
 
332
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
333
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
334
        root_row = (root_row_direntry, [])
 
335
        dirblocks = []
 
336
        # add a file in the root
 
337
        subdir_row = (['', 'subdir', 'directory', 'subdir-id', 0, packed_stat, ''], [])
 
338
        dirblocks.append(('', [subdir_row]))
 
339
        state._set_data([], root_row, dirblocks)
 
340
        self.assertEqual(['#bazaar dirstate flat format 1\n',
 
341
            'adler32: 1283137489\n',
 
342
            'num_entries: 2\n',
 
343
            '0\x00\n\x00'
 
344
            '0\x00\n\x00'
 
345
            '\x00\x00d\x00a-root-value\x000'
 
346
            '\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\x00\n\x00\x00subdir\x00'
 
347
            'd\x00subdir-id\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\x00'
 
348
            '\n\x00'],
 
349
            state.get_lines())
 
350
 
 
351
    def test_row_to_line(self):
 
352
        state = dirstate.DirState.initialize('dirstate')
 
353
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
354
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
355
        root_parent_direntries = []
 
356
        root_row = (root_row_direntry, root_parent_direntries)
 
357
        self.assertEqual('\x00\x00d\x00a-root-value\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00', state._row_to_line(root_row))
 
358
 
 
359
    def test_row_to_line_with_parent(self):
 
360
        state = dirstate.DirState.initialize('dirstate')
 
361
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
362
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
363
        # one parent that was a file at path /dirname/basename
 
364
        root_parent_direntries = [('revid', 'file', 'dirname', 'basename', 0, False, '')]
 
365
        root_row = (root_row_direntry, root_parent_direntries)
 
366
        self.assertEqual(
 
367
            '\x00\x00d\x00a-root-value\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
368
            '\x00revid\x00f\x00dirname\x00basename\x000\x00n\x00',
 
369
            state._row_to_line(root_row))
 
370
 
 
371
    def test_row_to_line_with_two_parents(self):
 
372
        state = dirstate.DirState.initialize('dirstate')
 
373
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
374
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
375
        # two parent entires: one that was a file at path /dirname/basename
 
376
        # and one that was a directory at /
 
377
        root_parent_direntries = [('revid', 'file', 'dirname', 'basename', 0, False, ''),
 
378
            ('revid2', 'directory', '', '', 0, False, '')]
 
379
        root_row = (root_row_direntry, root_parent_direntries)
 
380
        self.assertEqual(
 
381
            '\x00\x00d\x00a-root-value\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
382
            '\x00revid\x00f\x00dirname\x00basename\x000\x00n\x00'
 
383
            '\x00revid2\x00d\x00\x00\x000\x00n\x00',
 
384
            state._row_to_line(root_row))
 
385
 
 
386
    def test_iter_rows(self):
 
387
        # we should be able to iterate the dirstate rows from end to end
 
388
        # this is for get_lines to be easy to read.
 
389
        state = dirstate.DirState.initialize('dirstate')
 
390
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
391
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
392
        root_row = (root_row_direntry, [])
 
393
        dirblocks = []
 
394
        # add two files in the root
 
395
        subdir_row = (['', 'subdir', 'directory', 'subdir-id', 0, packed_stat, ''], [])
 
396
        afile_row = (['', 'afile', 'file', 'afile-id', 34, packed_stat, 'sha1value'], [])
 
397
        dirblocks.append(('', [subdir_row, afile_row]))
 
398
        # and one in subdir
 
399
        file_row2 = (['', '2file', 'file', '2file-id', 23, packed_stat, 'sha1value'], [])
 
400
        dirblocks.append(('subdir', [file_row2]))
 
401
        state._set_data([], root_row, dirblocks)
 
402
        expected_rows = [root_row, subdir_row, afile_row, file_row2]
 
403
        self.assertEqual(expected_rows, list(state._iter_rows()))