/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

Bypass irrelevant basis_inventory tests for dirstate.

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, errors
 
22
from bzrlib.dirstate import DirState
 
23
from bzrlib.memorytree import MemoryTree
 
24
from bzrlib.tests import TestCaseWithTransport
 
25
 
 
26
 
 
27
# TODO:
 
28
# test DirStateRevisionTree : test filtering out of deleted files does not
 
29
#         filter out files called RECYCLED.BIN ;)
 
30
# test 0 parents, 1 parent, 4 parents.
 
31
# test unicode parents, non unicode parents
 
32
# test all change permutations in one and two parents.
 
33
# i.e. file in parent 1, dir in parent 2, symlink in tree.
 
34
# test that renames in the tree result in correct parent paths 
 
35
# Test get state from a file, then asking for lines.
 
36
# write a smaller state, and check the file has been truncated.
 
37
# add a entry when its in state deleted
 
38
# revision attribute for root entries.
 
39
# test that utf8 strings are preserved in _row_to_line
 
40
# test parent manipulation 
 
41
# test parents that are null in save : i.e. no record in the parent tree for this.
 
42
# todo: _set_data records ghost parents.
 
43
# TESTS to write:
 
44
# general checks for NOT_IN_MEMORY error conditions.
 
45
# set_path_id on a NOT_IN_MEMORY dirstate
 
46
# set_path_id  unicode support
 
47
# set_path_id  setting id of a path not root
 
48
# set_path_id  setting id when there are parents without the id in the parents
 
49
# set_path_id  setting id when there are parents with the id in the parents
 
50
# set_path_id  setting id when state is not in memory
 
51
# set_path_id  setting id when state is in memory unmodified
 
52
# set_path_id  setting id when state is in memory modified
 
53
 
 
54
class TestTreeToDirstate(TestCaseWithTransport):
 
55
 
 
56
    def test_empty_to_dirstate(self):
 
57
        """We should be able to create a dirstate for an empty tree."""
 
58
        # There are no files on disk and no parents
 
59
        tree = self.make_branch_and_tree('tree')
 
60
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
61
        def check_state():
 
62
            # an inner function because there is no parameterisation at this point
 
63
            # if we make it reusable that would be a good thing.
 
64
            self.assertEqual([],  state.get_parent_ids())
 
65
            # there should be no ghosts in this tree.
 
66
            self.assertEqual([], state.get_ghosts())
 
67
            # there should be one fileid in this tree - the root of the tree.
 
68
            root_stat_pack = dirstate.pack_stat(os.stat(tree.basedir))
 
69
            self.assertEqual(
 
70
                [(('', '', 'directory', tree.inventory.root.file_id, 0, root_stat_pack, ''), [])],
 
71
                list(state._iter_rows()))
 
72
        check_state()
 
73
        state = dirstate.DirState.on_file('dirstate')
 
74
        check_state()
 
75
 
 
76
    def test_1_parents_empty_to_dirstate(self):
 
77
        # create a parent by doing a commit
 
78
        tree = self.make_branch_and_tree('tree')
 
79
        rev_id = tree.commit('first post')
 
80
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
81
        # we want to be able to get the lines of the dirstate that we will
 
82
        # write to disk.
 
83
        lines = state.get_lines()
 
84
        # we now have parent revisions, and all the files in the tree were
 
85
        # last modified in the parent.
 
86
        expected_lines_re = (
 
87
            '#bazaar dirstate flat format 1\n'
 
88
            'adler32: [0-9-][0-9]*\n'
 
89
            'num_entries: 1\n'
 
90
            '1\x00.*\x00\n\x00'
 
91
            '0\x00\n\x00'
 
92
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00\n'
 
93
            '\x00$') % rev_id.encode('utf8')
 
94
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
95
 
 
96
    def test_2_parents_empty_to_dirstate(self):
 
97
        # create a parent by doing a commit
 
98
        tree = self.make_branch_and_tree('tree')
 
99
        rev_id = tree.commit('first post')
 
100
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
101
        rev_id2 = tree2.commit('second post', allow_pointless=True)
 
102
        tree.merge_from_branch(tree2.branch)
 
103
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
104
        # we want to be able to get the lines of the dirstate that we will
 
105
        # write to disk.
 
106
        lines = state.get_lines()
 
107
        # we now have parent revisions, and all the files in the tree were
 
108
        # last modified in the parent.
 
109
        expected_lines_re = (
 
110
            '#bazaar dirstate flat format 1\n'
 
111
            'adler32: [0-9-][0-9]*\n'
 
112
            'num_entries: 1\n'
 
113
            '2\x00.*\x00.*\x00\n\x00'
 
114
            '0\x00\n\x00'
 
115
            '\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'
 
116
            '\x00$') % (rev_id.encode('utf8'), rev_id2.encode('utf8'))
 
117
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
118
        
 
119
    def test_empty_unknowns_are_ignored_to_dirstate(self):
 
120
        """We should be able to create a dirstate for an empty tree."""
 
121
        # There are no files on disk and no parents
 
122
        tree = self.make_branch_and_tree('tree')
 
123
        self.build_tree(['tree/unknown'])
 
124
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
125
        # we want to be able to get the lines of the dirstate that we will
 
126
        # write to disk.
 
127
        lines = state.get_lines()
 
128
        expected_lines_re = (
 
129
            '#bazaar dirstate flat format 1\n'
 
130
            'adler32: [0-9-][0-9]*\n'
 
131
            'num_entries: 1\n'
 
132
            '0\x00\n\x00'
 
133
            '0\x00\n\x00'
 
134
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00\n'
 
135
            '\x00$')
 
136
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
137
        
 
138
    def get_tree_with_a_file(self):
 
139
        tree = self.make_branch_and_tree('tree')
 
140
        self.build_tree(['tree/a file'])
 
141
        tree.add('a file', 'a file id')
 
142
        return tree
 
143
 
 
144
    def test_non_empty_no_parents_to_dirstate(self):
 
145
        """We should be able to create a dirstate for an empty tree."""
 
146
        # There are files on disk and no parents
 
147
        tree = self.get_tree_with_a_file()
 
148
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
149
        # we want to be able to get the lines of the dirstate that we will
 
150
        # write to disk.
 
151
        lines = state.get_lines()
 
152
        expected_lines_re = (
 
153
            '#bazaar dirstate flat format 1\n'
 
154
            'adler32: [0-9-][0-9]*\n'
 
155
            'num_entries: 2\n'
 
156
            '0\x00\n\x00'
 
157
            '0\x00\n\x00'
 
158
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00\n'
 
159
            '\x00\x00a file\x00f\x00a file id\x0024\x00[0-9a-zA-Z+/]{32}\x00c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8\x00'
 
160
            '\n\x00$')
 
161
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
162
 
 
163
    def test_1_parents_not_empty_to_dirstate(self):
 
164
        # create a parent by doing a commit
 
165
        tree = self.get_tree_with_a_file()
 
166
        rev_id = tree.commit('first post')
 
167
        # change the current content to be different this will alter stat, sha
 
168
        # and length:
 
169
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
170
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
171
        # we want to be able to get the lines of the dirstate that we will
 
172
        # write to disk.
 
173
        lines = state.get_lines()
 
174
        # we now have parent revisions, and all the files in the tree were
 
175
        # last modified in the parent.
 
176
        expected_lines_re = (
 
177
            '#bazaar dirstate flat format 1\n'
 
178
            'adler32: [0-9-][0-9]*\n'
 
179
            'num_entries: 2\n'
 
180
            '1\x00.*\x00\n\x00'
 
181
            '0\x00\n\x00'
 
182
            '\x00\x00d\x00TREE_ROOT\x00[0-9]+\x00[0-9a-zA-Z+/]{32}\x00\x00%s\x00d\x00\x00\x00\x00n\x00\x00\n'
 
183
            '\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'
 
184
            '\x00$')  % (rev_id.encode('utf8'), rev_id.encode('utf8'))
 
185
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
186
 
 
187
    def test_2_parents_not_empty_to_dirstate(self):
 
188
        # create a parent by doing a commit
 
189
        tree = self.get_tree_with_a_file()
 
190
        rev_id = tree.commit('first post')
 
191
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
192
        # change the current content to be different this will alter stat, sha
 
193
        # and length:
 
194
        self.build_tree_contents([('tree2/a file', 'merge content\n')])
 
195
        rev_id2 = tree2.commit('second post')
 
196
        tree.merge_from_branch(tree2.branch)
 
197
        # change the current content to be different this will alter stat, sha
 
198
        # and length again, giving us three distinct values:
 
199
        self.build_tree_contents([('tree/a file', 'new content\n')])
 
200
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
201
        # we want to be able to get the lines of the dirstate that we will
 
202
        # write to disk.
 
203
        lines = state.get_lines()
 
204
        # we now have parent revisions, and all the files in the tree were
 
205
        # last modified in the parent.
 
206
        expected_lines_re = (
 
207
            '#bazaar dirstate flat format 1\n'
 
208
            'adler32: [0-9-][0-9]*\n'
 
209
            'num_entries: 2\n'
 
210
            '2\x00.*\x00.*\x00\n\x00'
 
211
            '0\x00\n\x00'
 
212
            '\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'
 
213
            '\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$'
 
214
            % (rev_id.encode('utf8'), rev_id2.encode('utf8'),
 
215
               rev_id.encode('utf8'), rev_id2.encode('utf8')))
 
216
        self.assertContainsRe(''.join(lines), expected_lines_re)
 
217
 
 
218
 
 
219
class TestDirStateOnFile(TestCaseWithTransport):
 
220
 
 
221
    def test_construct_with_path(self):
 
222
        tree = self.make_branch_and_tree('tree')
 
223
        state = dirstate.DirState.from_tree(tree, 'dirstate')
 
224
        # we want to be able to get the lines of the dirstate that we will
 
225
        # write to disk.
 
226
        lines = state.get_lines()
 
227
        self.build_tree_contents([('dirstate', ''.join(lines))])
 
228
        # get a state object
 
229
        state = dirstate.DirState.on_file('dirstate')
 
230
        # ask it for a parents list
 
231
        self.assertEqual([], state.get_parent_ids())
 
232
        # doing a save should work here as there have been no changes.
 
233
        state.save()
 
234
 
 
235
 
 
236
class TestDirStateInitialize(TestCaseWithTransport):
 
237
 
 
238
    def test_initialize(self):
 
239
        state = dirstate.DirState.initialize('dirstate')
 
240
        self.assertIsInstance(state, dirstate.DirState)
 
241
        self.assertFileEqual(
 
242
            '#bazaar dirstate flat format 1\n'
 
243
            'adler32: -455929114\n'
 
244
            'num_entries: 1\n'
 
245
            '0\x00\n\x00'
 
246
            '0\x00\n\x00'
 
247
            # after the 0 parent count, there is the \x00\n\x00 line delim
 
248
            # then '' for dir, '' for basame, and then 'd' for directory.
 
249
            # then the root value, 0 size, our constant xxxx packed stat, and 
 
250
            # an empty sha value. Finally a new \x00\n\x00 delimiter
 
251
            '\x00\x00d\x00TREE_ROOT\x000\x00xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\x00\x00\n'
 
252
            '\x00',
 
253
            'dirstate')
 
254
        expected_rows = [
 
255
            (('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), [])]
 
256
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
257
        state = dirstate.DirState.on_file('dirstate')
 
258
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
259
 
 
260
 
 
261
class TestDirstateManipulations(TestCaseWithTransport):
 
262
 
 
263
    def test_add_ghost_tree(self):
 
264
        state = dirstate.DirState.initialize('dirstate')
 
265
        state.add_parent_tree('a-ghost', None)
 
266
        # now the parent list should be changed:
 
267
        self.assertEqual(['a-ghost'], state.get_parent_ids())
 
268
        self.assertEqual(['a-ghost'], state.get_ghosts())
 
269
        # save the state and reopen to check its persistent
 
270
        state.save()
 
271
        state = dirstate.DirState.on_file('dirstate')
 
272
        self.assertEqual(['a-ghost'], state.get_parent_ids())
 
273
        self.assertEqual(['a-ghost'], state.get_ghosts())
 
274
 
 
275
    def test_set_state_from_inventory_no_content_no_parents(self):
 
276
        # setting the current inventory is a slow but important api to support.
 
277
        state = dirstate.DirState.initialize('dirstate')
 
278
        tree1 = self.make_branch_and_memory_tree('tree1')
 
279
        tree1.lock_write()
 
280
        tree1.add('')
 
281
        revid1 = tree1.commit('foo')
 
282
        root_id = tree1.inventory.root.file_id
 
283
        state.set_state_from_inventory(tree1.inventory)
 
284
        tree1.unlock()
 
285
        self.assertEqual(DirState.IN_MEMORY_UNMODIFIED, state._header_state)
 
286
        self.assertEqual(DirState.IN_MEMORY_MODIFIED, state._dirblock_state)
 
287
        expected_rows = [(('', '', 'directory', root_id, 0, DirState.NULLSTAT, ''), [])]
 
288
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
289
        # check we can reopen and have the change preserved.
 
290
        state.save()
 
291
        state = dirstate.DirState.on_file('dirstate')
 
292
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
293
 
 
294
    def test_set_path_id_no_parents(self):
 
295
        """The id of a path can be changed trivally with no parents."""
 
296
        state = dirstate.DirState.initialize('dirstate')
 
297
        # check precondition to be sure the state does change appropriately.
 
298
        self.assertEqual(
 
299
            [(('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), [])],
 
300
            list(state._iter_rows()))
 
301
        state.set_path_id('', 'foobarbaz')
 
302
        expected_rows = [
 
303
            (('', '', 'directory', 'foobarbaz', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), [])]
 
304
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
305
        # should work across save too
 
306
        state.save()
 
307
        state = dirstate.DirState.on_file('dirstate')
 
308
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
309
 
 
310
    def test_set_parent_trees_no_content(self):
 
311
        # set_parent_trees is a slow but important api to support.
 
312
        state = dirstate.DirState.initialize('dirstate')
 
313
        tree1 = self.make_branch_and_memory_tree('tree1')
 
314
        tree1.lock_write()
 
315
        tree1.add('')
 
316
        revid1 = tree1.commit('foo')
 
317
        tree1.unlock()
 
318
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
 
319
        tree2 = MemoryTree.create_on_branch(branch2)
 
320
        tree2.lock_write()
 
321
        revid2 = tree2.commit('foo')
 
322
        root_id = tree2.inventory.root.file_id
 
323
        state.set_path_id('', root_id)
 
324
        tree2.unlock()
 
325
        state.set_parent_trees(
 
326
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
327
             (revid2, tree2.branch.repository.revision_tree(revid2)),
 
328
             ('ghost-rev', None)),
 
329
            ['ghost-rev'])
 
330
        # check we can reopen and use the dirstate after setting parent trees.
 
331
        state.save()
 
332
        state = dirstate.DirState.on_file('dirstate')
 
333
        self.assertEqual([revid1, revid2, 'ghost-rev'],  state.get_parent_ids())
 
334
        # iterating the entire state ensures that the state is parsable.
 
335
        list(state._iter_rows())
 
336
        # be sure that it sets not appends - change it
 
337
        state.set_parent_trees(
 
338
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
339
             ('ghost-rev', None)),
 
340
            ['ghost-rev'])
 
341
        # and now put it back.
 
342
        state.set_parent_trees(
 
343
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
344
             (revid2, tree2.branch.repository.revision_tree(revid2)),
 
345
             ('ghost-rev', tree2.branch.repository.revision_tree(None))),
 
346
            ['ghost-rev'])
 
347
        self.assertEqual([revid1, revid2, 'ghost-rev'],  state.get_parent_ids())
 
348
        # the ghost should be recorded as such by set_parent_trees.
 
349
        self.assertEqual(['ghost-rev'], state.get_ghosts())
 
350
        self.assertEqual(
 
351
            [(('', '', 'directory', root_id, 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), [
 
352
             (revid1, 'directory', '', '', 0, False, ''),
 
353
             (revid2, 'directory', '', '', 0, False, '')])],
 
354
            list(state._iter_rows()))
 
355
 
 
356
    def test_set_parent_trees_file_missing_from_tree(self):
 
357
        # Adding a parent tree may reference files not in the current state.
 
358
        # they should get listed just once by id, even if they are in two  
 
359
        # separate trees.
 
360
        # set_parent_trees is a slow but important api to support.
 
361
        state = dirstate.DirState.initialize('dirstate')
 
362
        tree1 = self.make_branch_and_memory_tree('tree1')
 
363
        tree1.lock_write()
 
364
        tree1.add('')
 
365
        tree1.add(['a file'], ['file-id'], ['file'])
 
366
        tree1.put_file_bytes_non_atomic('file-id', 'file-content')
 
367
        revid1 = tree1.commit('foo')
 
368
        tree1.unlock()
 
369
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
 
370
        tree2 = MemoryTree.create_on_branch(branch2)
 
371
        tree2.lock_write()
 
372
        tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
 
373
        revid2 = tree2.commit('foo')
 
374
        root_id = tree2.inventory.root.file_id
 
375
        state.set_path_id('', root_id)
 
376
        tree2.unlock()
 
377
        state.set_parent_trees(
 
378
            ((revid1, tree1.branch.repository.revision_tree(revid1)),
 
379
             (revid2, tree2.branch.repository.revision_tree(revid2)),
 
380
             ), [])
 
381
        # check the layout in memory
 
382
        expected_rows = [
 
383
            (('', '', 'directory', root_id, 0, DirState.NULLSTAT, ''),
 
384
             [(revid1.encode('utf8'), 'directory', '', '', 0, False, ''),
 
385
              (revid2.encode('utf8'), 'directory', '', '', 0, False, '')]),
 
386
            (('/', 'RECYCLED.BIN', 'file', 'file-id', 0, DirState.NULLSTAT, ''),
 
387
             [(revid1.encode('utf8'), 'file', '', 'a file', 12, False, '2439573625385400f2a669657a7db6ae7515d371'),
 
388
              (revid2.encode('utf8'), 'file', '', 'a file', 16, False, '542e57dc1cda4af37cb8e55ec07ce60364bb3c7d')])
 
389
            ]
 
390
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
391
        # check we can reopen and use the dirstate after setting parent trees.
 
392
        state.save()
 
393
        state = dirstate.DirState.on_file('dirstate')
 
394
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
395
 
 
396
    ### add a path via _set_data - so we dont need delta work, just
 
397
    # raw data in, and ensure that it comes out via get_lines happily.
 
398
 
 
399
    def test_add_path_to_root_no_parents_all_data(self):
 
400
        # The most trivial addition of a path is when there are no parents and
 
401
        # its in the root and all data about the file is supplied
 
402
        state = dirstate.DirState.initialize('dirstate')
 
403
        self.build_tree(['a file'])
 
404
        stat = os.lstat('a file')
 
405
        # the 1*20 is the sha1 pretend value.
 
406
        state.add('a file', 'a file id', 'file', stat, '1'*20)
 
407
        # having added it, it should be in the output of iter_rows.
 
408
        expected_rows = [
 
409
            (('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), []),
 
410
            (('', 'a file', 'file', 'a file id', 19, dirstate.pack_stat(stat), '1'*20), []),
 
411
            ]
 
412
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
413
        # saving and reloading should not affect this.
 
414
        state.save()
 
415
        state = dirstate.DirState.on_file('dirstate')
 
416
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
417
 
 
418
    def test_add_path_to_unversioned_directory(self):
 
419
        """Adding a path to an unversioned directory should error."""
 
420
        state = dirstate.DirState.initialize('dirstate')
 
421
        self.build_tree(['unversioned/', 'unversioned/a file'])
 
422
        self.assertRaises(errors.NoSuchFile, state.add, 'unversioned/a file',
 
423
            'a file id', 'file', None, None)
 
424
        
 
425
    def test_add_directory_to_root_no_parents_all_data(self):
 
426
        # The most trivial addition of a dir is when there are no parents and
 
427
        # its in the root and all data about the file is supplied
 
428
        state = dirstate.DirState.initialize('dirstate')
 
429
        self.build_tree(['a dir/'])
 
430
        stat = os.lstat('a dir')
 
431
        state.add('a dir', 'a dir id', 'directory', stat, None)
 
432
        # having added it, it should be in the output of iter_rows.
 
433
        expected_rows = [
 
434
            (('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), []),
 
435
            (('', 'a dir', 'directory', 'a dir id', 0, dirstate.pack_stat(stat), ''), []),
 
436
            ]
 
437
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
438
        # saving and reloading should not affect this.
 
439
        state.save()
 
440
        state = dirstate.DirState.on_file('dirstate')
 
441
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
442
 
 
443
    def test_add_symlink_to_root_no_parents_all_data(self):
 
444
        # The most trivial addition of a symlink when there are no parents and
 
445
        # its in the root and all data about the file is supplied
 
446
        state = dirstate.DirState.initialize('dirstate')
 
447
        ## TODO: windows: dont fail this test. Also, how are symlinks meant to
 
448
        # be represented on windows.
 
449
        os.symlink('target', 'a link')
 
450
        stat = os.lstat('a link')
 
451
        state.add('a link', 'a link id', 'symlink', stat, 'target')
 
452
        # having added it, it should be in the output of iter_rows.
 
453
        expected_rows = [
 
454
            (('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), []),
 
455
            (('', 'a link', 'symlink', 'a link id', 6, dirstate.pack_stat(stat), 'target'), []),
 
456
            ]
 
457
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
458
        # saving and reloading should not affect this.
 
459
        state.save()
 
460
        state = dirstate.DirState.on_file('dirstate')
 
461
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
462
 
 
463
    def test_add_directory_and_child_no_parents_all_data(self):
 
464
        # after adding a directory, we should be able to add children to it.
 
465
        state = dirstate.DirState.initialize('dirstate')
 
466
        self.build_tree(['a dir/', 'a dir/a file'])
 
467
        stat = os.lstat('a dir')
 
468
        state.add('a dir', 'a dir id', 'directory', stat, None)
 
469
        filestat = os.lstat('a dir/a file')
 
470
        state.add('a dir/a file', 'a file id', 'file', filestat, '1'*20)
 
471
        # having added it, it should be in the output of iter_rows.
 
472
        expected_rows = [
 
473
            (('', '', 'directory', 'TREE_ROOT', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', ''), []),
 
474
            (('', 'a dir', 'directory', 'a dir id', 0, dirstate.pack_stat(stat), ''), []),
 
475
            (('a dir', 'a file', 'file', 'a file id', 25, dirstate.pack_stat(filestat), '1'*20), []),
 
476
            ]
 
477
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
478
        # saving and reloading should not affect this.
 
479
        state.save()
 
480
        state = dirstate.DirState.on_file('dirstate')
 
481
        self.assertEqual(expected_rows, list(state._iter_rows()))
 
482
 
 
483
 
 
484
class TestGetLines(TestCaseWithTransport):
 
485
 
 
486
    def test_adding_ghost_tree_sets_ghosts_line(self):
 
487
        state = dirstate.DirState.initialize('dirstate')
 
488
        state.add_parent_tree('a-ghost', None)
 
489
        self.assertEqual(['#bazaar dirstate flat format 1\n',
 
490
            'adler32: 1202264142\n',
 
491
            'num_entries: 1\n',
 
492
            '1\x00a-ghost\x00\n\x00'
 
493
            '1\x00a-ghost\x00\n\x00'
 
494
            '\x00\x00d\x00TREE_ROOT\x000\x00'
 
495
            'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\x00\x00\n\x00'],
 
496
            state.get_lines())
 
497
 
 
498
    def test_adding_tree_changes_lines(self):
 
499
        state = dirstate.DirState.initialize('dirstate')
 
500
        lines = list(state.get_lines())
 
501
        state.add_parent_tree('a-ghost', None)
 
502
        self.assertNotEqual(lines, state.get_lines())
 
503
 
 
504
    def test_get_line_with_2_rows(self):
 
505
        state = dirstate.DirState.initialize('dirstate')
 
506
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
507
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
508
        root_row = (root_row_direntry, [])
 
509
        dirblocks = []
 
510
        # add a file in the root
 
511
        subdir_row = (['', 'subdir', 'directory', 'subdir-id', 0, packed_stat, ''], [])
 
512
        dirblocks.append(('', [subdir_row]))
 
513
        state._set_data([], root_row, dirblocks)
 
514
        self.assertEqual(['#bazaar dirstate flat format 1\n',
 
515
            'adler32: 1283137489\n',
 
516
            'num_entries: 2\n',
 
517
            '0\x00\n\x00'
 
518
            '0\x00\n\x00'
 
519
            '\x00\x00d\x00a-root-value\x000'
 
520
            '\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\x00\n\x00\x00subdir\x00'
 
521
            'd\x00subdir-id\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\x00'
 
522
            '\n\x00'],
 
523
            state.get_lines())
 
524
 
 
525
    def test_row_to_line(self):
 
526
        state = dirstate.DirState.initialize('dirstate')
 
527
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
528
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
529
        root_parent_direntries = []
 
530
        root_row = (root_row_direntry, root_parent_direntries)
 
531
        self.assertEqual('\x00\x00d\x00a-root-value\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00', state._row_to_line(root_row))
 
532
 
 
533
    def test_row_to_line_with_parent(self):
 
534
        state = dirstate.DirState.initialize('dirstate')
 
535
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
536
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
537
        # one parent that was a file at path /dirname/basename
 
538
        root_parent_direntries = [('revid', 'file', 'dirname', 'basename', 0, False, '')]
 
539
        root_row = (root_row_direntry, root_parent_direntries)
 
540
        self.assertEqual(
 
541
            '\x00\x00d\x00a-root-value\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
542
            '\x00revid\x00f\x00dirname\x00basename\x000\x00n\x00',
 
543
            state._row_to_line(root_row))
 
544
 
 
545
    def test_row_to_line_with_two_parents(self):
 
546
        state = dirstate.DirState.initialize('dirstate')
 
547
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
548
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
549
        # two parent entires: one that was a file at path /dirname/basename
 
550
        # and one that was a directory at /
 
551
        root_parent_direntries = [('revid', 'file', 'dirname', 'basename', 0, False, ''),
 
552
            ('revid2', 'directory', '', '', 0, False, '')]
 
553
        root_row = (root_row_direntry, root_parent_direntries)
 
554
        self.assertEqual(
 
555
            '\x00\x00d\x00a-root-value\x000\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
 
556
            '\x00revid\x00f\x00dirname\x00basename\x000\x00n\x00'
 
557
            '\x00revid2\x00d\x00\x00\x000\x00n\x00',
 
558
            state._row_to_line(root_row))
 
559
 
 
560
    def test_iter_rows(self):
 
561
        # we should be able to iterate the dirstate rows from end to end
 
562
        # this is for get_lines to be easy to read.
 
563
        state = dirstate.DirState.initialize('dirstate')
 
564
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
 
565
        root_row_direntry = ('', '', 'directory', 'a-root-value', 0, packed_stat, '')
 
566
        root_row = (root_row_direntry, [])
 
567
        dirblocks = []
 
568
        # add two files in the root
 
569
        subdir_row = (['', 'subdir', 'directory', 'subdir-id', 0, packed_stat, ''], [])
 
570
        afile_row = (['', 'afile', 'file', 'afile-id', 34, packed_stat, 'sha1value'], [])
 
571
        dirblocks.append(('', [subdir_row, afile_row]))
 
572
        # and one in subdir
 
573
        file_row2 = (['', '2file', 'file', '2file-id', 23, packed_stat, 'sha1value'], [])
 
574
        dirblocks.append(('subdir', [file_row2]))
 
575
        state._set_data([], root_row, dirblocks)
 
576
        expected_rows = [root_row, subdir_row, afile_row, file_row2]
 
577
        self.assertEqual(expected_rows, list(state._iter_rows()))