~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-07 16:05:27 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060607160527-2b3649154d0e2e84
more code cleanup

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
#
 
12
 
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from bzrlib import errors, inventory, osutils
 
17
from cStringIO import StringIO
 
18
import os
 
19
 
 
20
from bzrlib.branch import Branch
 
21
import bzrlib.errors as errors
 
22
from bzrlib.diff import internal_diff
18
23
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
19
 
    InventoryDirectory, InventoryEntry, TreeReference)
20
 
from bzrlib.osutils import (pathjoin, is_inside_any, 
21
 
    is_inside_or_parent_of_any)
22
 
from bzrlib.tests import TestCase
 
24
    InventoryDirectory, InventoryEntry)
 
25
import bzrlib.inventory as inventory
 
26
from bzrlib.osutils import has_symlinks, rename, pathjoin
 
27
from bzrlib.tests import TestCase, TestCaseWithTransport
 
28
from bzrlib.transform import TreeTransform
 
29
from bzrlib.uncommit import uncommit
23
30
 
24
31
 
25
32
class TestInventory(TestCase):
26
33
 
27
 
    def test_add_path(self):
28
 
 
29
 
        inv = Inventory(root_id=None)
30
 
        self.assertIs(None, inv.root)
31
 
        ie = inv.add_path("", "directory", "my-root")
32
 
        self.assertEqual("my-root", ie.file_id)
33
 
        self.assertIs(ie, inv.root)
34
 
 
35
34
    def test_is_within(self):
 
35
        from bzrlib.osutils import is_inside_any
36
36
 
37
37
        SRC_FOO_C = pathjoin('src', 'foo.c')
38
38
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
44
44
        for dirs, fn in [(['src'], 'srccontrol'),
45
45
                         (['src'], 'srccontrol/foo')]:
46
46
            self.assertFalse(is_inside_any(dirs, fn))
47
 
 
48
 
    def test_is_within_or_parent(self):
49
 
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
50
 
                         (['src'], 'src/foo.c'),
51
 
                         (['src/bar.c'], 'src'),
52
 
                         (['src/bar.c', 'bla/foo.c'], 'src'),
53
 
                         (['src'], 'src'),
54
 
                         ]:
55
 
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
56
47
            
57
 
        for dirs, fn in [(['src'], 'srccontrol'),
58
 
                         (['srccontrol/foo.c'], 'src'),
59
 
                         (['src'], 'srccontrol/foo')]:
60
 
            self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
61
 
 
62
48
    def test_ids(self):
63
49
        """Test detection of files within selected directories."""
64
50
        inv = Inventory()
75
61
        
76
62
        self.assert_('src-id' in inv)
77
63
 
78
 
    def test_non_directory_children(self):
79
 
        """Test path2id when a parent directory has no children"""
80
 
        inv = inventory.Inventory('tree_root')
81
 
        inv.add(inventory.InventoryFile('file-id','file', 
82
 
                                        parent_id='tree_root'))
83
 
        inv.add(inventory.InventoryLink('link-id','link', 
84
 
                                        parent_id='tree_root'))
85
 
        self.assertIs(None, inv.path2id('file/subfile'))
86
 
        self.assertIs(None, inv.path2id('link/subfile'))
87
 
 
88
64
    def test_iter_entries(self):
89
65
        inv = Inventory()
90
66
        
96
72
            inv.add_path(*args)
97
73
 
98
74
        self.assertEqual([
99
 
            ('', ROOT_ID),
100
75
            ('Makefile', 'makefile-id'),
101
76
            ('doc', 'doc-id'),
102
77
            ('src', 'src-id'),
104
79
            ('src/hello.c', 'hello-id'),
105
80
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
106
81
            
107
 
    def test_iter_entries_by_dir(self):
108
 
        inv = Inventory()
109
 
        
110
 
        for args in [('src', 'directory', 'src-id'), 
111
 
                     ('doc', 'directory', 'doc-id'), 
112
 
                     ('src/hello.c', 'file', 'hello-id'),
113
 
                     ('src/bye.c', 'file', 'bye-id'),
114
 
                     ('zz', 'file', 'zz-id'),
115
 
                     ('src/sub/', 'directory', 'sub-id'),
116
 
                     ('src/zz.c', 'file', 'zzc-id'),
117
 
                     ('src/sub/a', 'file', 'a-id'),
118
 
                     ('Makefile', 'file', 'makefile-id')]:
119
 
            inv.add_path(*args)
120
 
 
121
 
        self.assertEqual([
122
 
            ('', ROOT_ID),
123
 
            ('Makefile', 'makefile-id'),
124
 
            ('doc', 'doc-id'),
125
 
            ('src', 'src-id'),
126
 
            ('zz', 'zz-id'),
127
 
            ('src/bye.c', 'bye-id'),
128
 
            ('src/hello.c', 'hello-id'),
129
 
            ('src/sub', 'sub-id'),
130
 
            ('src/zz.c', 'zzc-id'),
131
 
            ('src/sub/a', 'a-id'),
132
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir()])
133
 
            
134
 
        self.assertEqual([
135
 
            ('', ROOT_ID),
136
 
            ('Makefile', 'makefile-id'),
137
 
            ('doc', 'doc-id'),
138
 
            ('src', 'src-id'),
139
 
            ('zz', 'zz-id'),
140
 
            ('src/bye.c', 'bye-id'),
141
 
            ('src/hello.c', 'hello-id'),
142
 
            ('src/sub', 'sub-id'),
143
 
            ('src/zz.c', 'zzc-id'),
144
 
            ('src/sub/a', 'a-id'),
145
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
146
 
                specific_file_ids=('a-id', 'zzc-id', 'doc-id', ROOT_ID,
147
 
                'hello-id', 'bye-id', 'zz-id', 'src-id', 'makefile-id', 
148
 
                'sub-id'))])
149
 
 
150
 
        self.assertEqual([
151
 
            ('Makefile', 'makefile-id'),
152
 
            ('doc', 'doc-id'),
153
 
            ('zz', 'zz-id'),
154
 
            ('src/bye.c', 'bye-id'),
155
 
            ('src/hello.c', 'hello-id'),
156
 
            ('src/zz.c', 'zzc-id'),
157
 
            ('src/sub/a', 'a-id'),
158
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
159
 
                specific_file_ids=('a-id', 'zzc-id', 'doc-id',
160
 
                'hello-id', 'bye-id', 'zz-id', 'makefile-id'))])
161
 
 
162
 
        self.assertEqual([
163
 
            ('Makefile', 'makefile-id'),
164
 
            ('src/bye.c', 'bye-id'),
165
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
166
 
                specific_file_ids=('bye-id', 'makefile-id'))])
167
 
 
168
 
        self.assertEqual([
169
 
            ('Makefile', 'makefile-id'),
170
 
            ('src/bye.c', 'bye-id'),
171
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
172
 
                specific_file_ids=('bye-id', 'makefile-id'))])
173
 
 
174
 
        self.assertEqual([
175
 
            ('src/bye.c', 'bye-id'),
176
 
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
177
 
                specific_file_ids=('bye-id',))])
178
 
 
179
 
    def test_add_recursive(self):
180
 
        parent = InventoryDirectory('src-id', 'src', ROOT_ID)
181
 
        child = InventoryFile('hello-id', 'hello.c', 'src-id')
182
 
        parent.children[child.file_id] = child
183
 
        inv = Inventory()
184
 
        inv.add(parent)
185
 
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
 
82
    def test_version(self):
 
83
        """Inventory remembers the text's version."""
 
84
        inv = Inventory()
 
85
        ie = inv.add_path('foo.txt', 'file')
 
86
        ## XXX
186
87
 
187
88
 
188
89
class TestInventoryEntry(TestCase):
258
159
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
259
160
            inventory.InventoryDirectory)
260
161
 
261
 
    def test_make_entry_non_normalized(self):
262
 
        orig_normalized_filename = osutils.normalized_filename
263
 
 
264
 
        try:
265
 
            osutils.normalized_filename = osutils._accessible_normalized_filename
266
 
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
267
 
            self.assertEqual(u'\xe5', entry.name)
268
 
            self.assertIsInstance(entry, inventory.InventoryFile)
269
 
 
270
 
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
271
 
            self.assertRaises(errors.InvalidNormalization,
272
 
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
273
 
        finally:
274
 
            osutils.normalized_filename = orig_normalized_filename
 
162
class TestEntryDiffing(TestCaseWithTransport):
 
163
 
 
164
    def setUp(self):
 
165
        super(TestEntryDiffing, self).setUp()
 
166
        self.wt = self.make_branch_and_tree('.')
 
167
        self.branch = self.wt.branch
 
168
        print >> open('file', 'wb'), 'foo'
 
169
        print >> open('binfile', 'wb'), 'foo'
 
170
        self.wt.add(['file'], ['fileid'])
 
171
        self.wt.add(['binfile'], ['binfileid'])
 
172
        if has_symlinks():
 
173
            os.symlink('target1', 'symlink')
 
174
            self.wt.add(['symlink'], ['linkid'])
 
175
        self.wt.commit('message_1', rev_id = '1')
 
176
        print >> open('file', 'wb'), 'bar'
 
177
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
 
178
        if has_symlinks():
 
179
            os.unlink('symlink')
 
180
            os.symlink('target2', 'symlink')
 
181
        self.tree_1 = self.branch.repository.revision_tree('1')
 
182
        self.inv_1 = self.branch.repository.get_inventory('1')
 
183
        self.file_1 = self.inv_1['fileid']
 
184
        self.file_1b = self.inv_1['binfileid']
 
185
        self.tree_2 = self.wt
 
186
        self.inv_2 = self.tree_2.read_working_inventory()
 
187
        self.file_2 = self.inv_2['fileid']
 
188
        self.file_2b = self.inv_2['binfileid']
 
189
        if has_symlinks():
 
190
            self.link_1 = self.inv_1['linkid']
 
191
            self.link_2 = self.inv_2['linkid']
 
192
 
 
193
    def test_file_diff_deleted(self):
 
194
        output = StringIO()
 
195
        self.file_1.diff(internal_diff, 
 
196
                          "old_label", self.tree_1,
 
197
                          "/dev/null", None, None,
 
198
                          output)
 
199
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
200
                                            "+++ /dev/null\n"
 
201
                                            "@@ -1,1 +0,0 @@\n"
 
202
                                            "-foo\n"
 
203
                                            "\n")
 
204
 
 
205
    def test_file_diff_added(self):
 
206
        output = StringIO()
 
207
        self.file_1.diff(internal_diff, 
 
208
                          "new_label", self.tree_1,
 
209
                          "/dev/null", None, None,
 
210
                          output, reverse=True)
 
211
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
212
                                            "+++ new_label\n"
 
213
                                            "@@ -0,0 +1,1 @@\n"
 
214
                                            "+foo\n"
 
215
                                            "\n")
 
216
 
 
217
    def test_file_diff_changed(self):
 
218
        output = StringIO()
 
219
        self.file_1.diff(internal_diff, 
 
220
                          "/dev/null", self.tree_1, 
 
221
                          "new_label", self.file_2, self.tree_2,
 
222
                          output)
 
223
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
224
                                            "+++ new_label\n"
 
225
                                            "@@ -1,1 +1,1 @@\n"
 
226
                                            "-foo\n"
 
227
                                            "+bar\n"
 
228
                                            "\n")
 
229
        
 
230
    def test_file_diff_binary(self):
 
231
        output = StringIO()
 
232
        self.file_1.diff(internal_diff, 
 
233
                          "/dev/null", self.tree_1, 
 
234
                          "new_label", self.file_2b, self.tree_2,
 
235
                          output)
 
236
        self.assertEqual(output.getvalue(), 
 
237
                         "Binary files /dev/null and new_label differ\n")
 
238
    def test_link_diff_deleted(self):
 
239
        if not has_symlinks():
 
240
            return
 
241
        output = StringIO()
 
242
        self.link_1.diff(internal_diff, 
 
243
                          "old_label", self.tree_1,
 
244
                          "/dev/null", None, None,
 
245
                          output)
 
246
        self.assertEqual(output.getvalue(),
 
247
                         "=== target was 'target1'\n")
 
248
 
 
249
    def test_link_diff_added(self):
 
250
        if not has_symlinks():
 
251
            return
 
252
        output = StringIO()
 
253
        self.link_1.diff(internal_diff, 
 
254
                          "new_label", self.tree_1,
 
255
                          "/dev/null", None, None,
 
256
                          output, reverse=True)
 
257
        self.assertEqual(output.getvalue(),
 
258
                         "=== target is 'target1'\n")
 
259
 
 
260
    def test_link_diff_changed(self):
 
261
        if not has_symlinks():
 
262
            return
 
263
        output = StringIO()
 
264
        self.link_1.diff(internal_diff, 
 
265
                          "/dev/null", self.tree_1, 
 
266
                          "new_label", self.link_2, self.tree_2,
 
267
                          output)
 
268
        self.assertEqual(output.getvalue(),
 
269
                         "=== target changed 'target1' => 'target2'\n")
 
270
 
 
271
 
 
272
class TestSnapshot(TestCaseWithTransport):
 
273
 
 
274
    def setUp(self):
 
275
        # for full testing we'll need a branch
 
276
        # with a subdir to test parent changes.
 
277
        # and a file, link and dir under that.
 
278
        # but right now I only need one attribute
 
279
        # to change, and then test merge patterns
 
280
        # with fake parent entries.
 
281
        super(TestSnapshot, self).setUp()
 
282
        self.wt = self.make_branch_and_tree('.')
 
283
        self.branch = self.wt.branch
 
284
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
 
285
        self.wt.add(['subdir', 'subdir/file'],
 
286
                                       ['dirid', 'fileid'])
 
287
        if has_symlinks():
 
288
            pass
 
289
        self.wt.commit('message_1', rev_id = '1')
 
290
        self.tree_1 = self.branch.repository.revision_tree('1')
 
291
        self.inv_1 = self.branch.repository.get_inventory('1')
 
292
        self.file_1 = self.inv_1['fileid']
 
293
        self.file_active = self.wt.inventory['fileid']
 
294
 
 
295
    def test_snapshot_new_revision(self):
 
296
        # This tests that a simple commit with no parents makes a new
 
297
        # revision value in the inventory entry
 
298
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, 
 
299
                                  self.branch.repository.weave_store,
 
300
                                  self.branch.get_transaction())
 
301
        # expected outcome - file_1 has a revision id of '2', and we can get
 
302
        # its text of 'file contents' out of the weave.
 
303
        self.assertEqual(self.file_1.revision, '1')
 
304
        self.assertEqual(self.file_active.revision, '2')
 
305
        # this should be a separate test probably, but lets check it once..
 
306
        lines = self.branch.repository.weave_store.get_weave(
 
307
            'fileid', 
 
308
            self.branch.get_transaction()).get_lines('2')
 
309
        self.assertEqual(lines, ['contents of subdir/file\n'])
 
310
 
 
311
    def test_snapshot_unchanged(self):
 
312
        #This tests that a simple commit does not make a new entry for
 
313
        # an unchanged inventory entry
 
314
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
 
315
                                  self.wt, 
 
316
                                  self.branch.repository.weave_store,
 
317
                                  self.branch.get_transaction())
 
318
        self.assertEqual(self.file_1.revision, '1')
 
319
        self.assertEqual(self.file_active.revision, '1')
 
320
        vf = self.branch.repository.weave_store.get_weave(
 
321
            'fileid', 
 
322
            self.branch.repository.get_transaction())
 
323
        self.assertRaises(errors.RevisionNotPresent,
 
324
                          vf.get_lines,
 
325
                          '2')
 
326
 
 
327
    def test_snapshot_merge_identical_different_revid(self):
 
328
        # This tests that a commit with two identical parents, one of which has
 
329
        # a different revision id, results in a new revision id in the entry.
 
330
        # 1->other, commit a merge of other against 1, results in 2.
 
331
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
 
332
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
 
333
        other_ie.revision = '1'
 
334
        other_ie.text_sha1 = self.file_1.text_sha1
 
335
        other_ie.text_size = self.file_1.text_size
 
336
        self.assertEqual(self.file_1, other_ie)
 
337
        other_ie.revision = 'other'
 
338
        self.assertNotEqual(self.file_1, other_ie)
 
339
        versionfile = self.branch.repository.weave_store.get_weave(
 
340
            'fileid', self.branch.repository.get_transaction())
 
341
        versionfile.clone_text('other', '1', ['1'])
 
342
        self.file_active.snapshot('2', 'subdir/file', 
 
343
                                  {'1':self.file_1, 'other':other_ie},
 
344
                                  self.wt, 
 
345
                                  self.branch.repository.weave_store,
 
346
                                  self.branch.get_transaction())
 
347
        self.assertEqual(self.file_active.revision, '2')
 
348
 
 
349
    def test_snapshot_changed(self):
 
350
        # This tests that a commit with one different parent results in a new
 
351
        # revision id in the entry.
 
352
        self.file_active.name='newname'
 
353
        rename('subdir/file', 'subdir/newname')
 
354
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
 
355
                                  self.wt,
 
356
                                  self.branch.repository.weave_store,
 
357
                                  self.branch.get_transaction())
 
358
        # expected outcome - file_1 has a revision id of '2'
 
359
        self.assertEqual(self.file_active.revision, '2')
 
360
 
 
361
 
 
362
class TestPreviousHeads(TestCaseWithTransport):
 
363
 
 
364
    def setUp(self):
 
365
        # we want several inventories, that respectively
 
366
        # give use the following scenarios:
 
367
        # A) fileid not in any inventory (A),
 
368
        # B) fileid present in one inventory (B) and (A,B)
 
369
        # C) fileid present in two inventories, and they
 
370
        #   are not mutual descendents (B, C)
 
371
        # D) fileid present in two inventories and one is
 
372
        #   a descendent of the other. (B, D)
 
373
        super(TestPreviousHeads, self).setUp()
 
374
        self.wt = self.make_branch_and_tree('.')
 
375
        self.branch = self.wt.branch
 
376
        self.build_tree(['file'])
 
377
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
 
378
        self.inv_A = self.branch.repository.get_inventory('A')
 
379
        self.wt.add(['file'], ['fileid'])
 
380
        self.wt.commit('add file', rev_id='B')
 
381
        self.inv_B = self.branch.repository.get_inventory('B')
 
382
        uncommit(self.branch, tree=self.wt)
 
383
        self.assertEqual(self.branch.revision_history(), ['A'])
 
384
        self.wt.commit('another add of file', rev_id='C')
 
385
        self.inv_C = self.branch.repository.get_inventory('C')
 
386
        self.wt.add_pending_merge('B')
 
387
        self.wt.commit('merge in B', rev_id='D')
 
388
        self.inv_D = self.branch.repository.get_inventory('D')
 
389
        self.file_active = self.wt.inventory['fileid']
 
390
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
391
            self.branch.repository.get_transaction())
 
392
        
 
393
    def get_previous_heads(self, inventories):
 
394
        return self.file_active.find_previous_heads(
 
395
            inventories, 
 
396
            self.branch.repository.weave_store,
 
397
            self.branch.repository.get_transaction())
 
398
        
 
399
    def test_fileid_in_no_inventory(self):
 
400
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
 
401
 
 
402
    def test_fileid_in_one_inventory(self):
 
403
        self.assertEqual({'B':self.inv_B['fileid']},
 
404
                         self.get_previous_heads([self.inv_B]))
 
405
        self.assertEqual({'B':self.inv_B['fileid']},
 
406
                         self.get_previous_heads([self.inv_A, self.inv_B]))
 
407
        self.assertEqual({'B':self.inv_B['fileid']},
 
408
                         self.get_previous_heads([self.inv_B, self.inv_A]))
 
409
 
 
410
    def test_fileid_in_two_inventories_gives_both_entries(self):
 
411
        self.assertEqual({'B':self.inv_B['fileid'],
 
412
                          'C':self.inv_C['fileid']},
 
413
                          self.get_previous_heads([self.inv_B, self.inv_C]))
 
414
        self.assertEqual({'B':self.inv_B['fileid'],
 
415
                          'C':self.inv_C['fileid']},
 
416
                          self.get_previous_heads([self.inv_C, self.inv_B]))
 
417
 
 
418
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
 
419
        self.assertEqual({'D':self.inv_D['fileid']},
 
420
                         self.get_previous_heads([self.inv_B, self.inv_D]))
 
421
        self.assertEqual({'D':self.inv_D['fileid']},
 
422
                         self.get_previous_heads([self.inv_D, self.inv_B]))
 
423
 
 
424
    # TODO: test two inventories with the same file revision 
275
425
 
276
426
 
277
427
class TestDescribeChanges(TestCase):
332
482
        self.assertEqual(expected_change, change)
333
483
 
334
484
 
335
 
class TestIsRoot(TestCase):
336
 
    """Ensure our root-checking code is accurate."""
337
 
 
338
 
    def test_is_root(self):
339
 
        inv = Inventory('TREE_ROOT')
340
 
        self.assertTrue(inv.is_root('TREE_ROOT'))
341
 
        self.assertFalse(inv.is_root('booga'))
342
 
        inv.root.file_id = 'booga'
343
 
        self.assertFalse(inv.is_root('TREE_ROOT'))
344
 
        self.assertTrue(inv.is_root('booga'))
345
 
        # works properly even if no root is set
346
 
        inv.root = None
347
 
        self.assertFalse(inv.is_root('TREE_ROOT'))
348
 
        self.assertFalse(inv.is_root('booga'))
349
 
 
350
 
 
351
 
class TestTreeReference(TestCase):
352
 
    
353
 
    def test_create(self):
354
 
        inv = Inventory('tree-root-123')
355
 
        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
356
 
                              revision='rev', reference_revision='rev2'))
357
 
 
358
 
 
359
 
class TestEncoding(TestCase):
360
 
 
361
 
    def test_error_encoding(self):
362
 
        inv = Inventory('tree-root')
363
 
        inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
364
 
        try:
365
 
            inv.add(InventoryFile('b-id', u'\u1234', 'tree-root'))
366
 
        except errors.BzrError, e:
367
 
            self.assertContainsRe(str(e), u'\u1234'.encode('utf-8'))
368
 
        else:
369
 
            self.fail('BzrError not raised')
 
485
class TestExecutable(TestCaseWithTransport):
 
486
 
 
487
    def test_stays_executable(self):
 
488
        a_id = "a-20051208024829-849e76f7968d7a86"
 
489
        b_id = "b-20051208024829-849e76f7968d7a86"
 
490
        wt = self.make_branch_and_tree('b1')
 
491
        b = wt.branch
 
492
        tt = TreeTransform(wt)
 
493
        tt.new_file('a', tt.root, 'a test\n', a_id, True)
 
494
        tt.new_file('b', tt.root, 'b test\n', b_id, False)
 
495
        tt.apply()
 
496
 
 
497
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
498
 
 
499
        # reopen the tree and ensure it stuck.
 
500
        wt = wt.bzrdir.open_workingtree()
 
501
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
 
502
 
 
503
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
504
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
 
505
 
 
506
        wt.commit('adding a,b', rev_id='r1')
 
507
 
 
508
        rev_tree = b.repository.revision_tree('r1')
 
509
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
 
510
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
 
511
 
 
512
        self.failUnless(rev_tree.inventory[a_id].executable)
 
513
        self.failIf(rev_tree.inventory[b_id].executable)
 
514
 
 
515
        # Make sure the entries are gone
 
516
        os.remove('b1/a')
 
517
        os.remove('b1/b')
 
518
        self.failIf(wt.has_id(a_id))
 
519
        self.failIf(wt.has_filename('a'))
 
520
        self.failIf(wt.has_id(b_id))
 
521
        self.failIf(wt.has_filename('b'))
 
522
 
 
523
        # Make sure that revert is able to bring them back,
 
524
        # and sets 'a' back to being executable
 
525
 
 
526
        wt.revert(['a', 'b'], rev_tree, backups=False)
 
527
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
 
528
 
 
529
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
530
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
 
531
 
 
532
        # Now remove them again, and make sure that after a
 
533
        # commit, they are still marked correctly
 
534
        os.remove('b1/a')
 
535
        os.remove('b1/b')
 
536
        wt.commit('removed', rev_id='r2')
 
537
 
 
538
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
 
539
        self.failIf(wt.has_id(a_id))
 
540
        self.failIf(wt.has_filename('a'))
 
541
        self.failIf(wt.has_id(b_id))
 
542
        self.failIf(wt.has_filename('b'))
 
543
 
 
544
        # Now revert back to the previous commit
 
545
        wt.revert([], rev_tree, backups=False)
 
546
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
 
547
 
 
548
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
549
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
 
550
 
 
551
        # Now make sure that 'bzr branch' also preserves the
 
552
        # executable bit
 
553
        # TODO: Maybe this should be a blackbox test
 
554
        d2 = b.bzrdir.clone('b2', revision_id='r1')
 
555
        t2 = d2.open_workingtree()
 
556
        b2 = t2.branch
 
557
        self.assertEquals('r1', b2.last_revision())
 
558
 
 
559
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
 
560
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
 
561
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
 
562
 
 
563
        # Make sure pull will delete the files
 
564
        t2.pull(b)
 
565
        self.assertEquals('r2', b2.last_revision())
 
566
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
 
567
 
 
568
        # Now commit the changes on the first branch
 
569
        # so that the second branch can pull the changes
 
570
        # and make sure that the executable bit has been copied
 
571
        wt.commit('resurrected', rev_id='r3')
 
572
 
 
573
        t2.pull(b)
 
574
        self.assertEquals('r3', b2.last_revision())
 
575
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
 
576
 
 
577
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
 
578
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
 
579
 
 
580
 
 
581
class TestRevert(TestCaseWithTransport):
 
582
 
 
583
    def test_dangling_id(self):
 
584
        wt = self.make_branch_and_tree('b1')
 
585
        self.assertEqual(len(wt.inventory), 1)
 
586
        open('b1/a', 'wb').write('a test\n')
 
587
        wt.add('a')
 
588
        self.assertEqual(len(wt.inventory), 2)
 
589
        os.unlink('b1/a')
 
590
        wt.revert([])
 
591
        self.assertEqual(len(wt.inventory), 1)