~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-12 17:30:53 UTC
  • mfrom: (2338.3.1 hide-nested)
  • Revision ID: pqm@pqm.ubuntu.com-20070312173053-4cdb4cd14190d29e
Hide nested-tree commands and improve their docs

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006 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
17
from cStringIO import StringIO
18
18
import os
 
19
import time
19
20
 
 
21
from bzrlib import errors, inventory, osutils
20
22
from bzrlib.branch import Branch
21
 
import bzrlib.errors as errors
22
23
from bzrlib.diff import internal_diff
23
 
from bzrlib.inventory import Inventory, ROOT_ID
24
 
import bzrlib.inventory as inventory
25
 
from bzrlib.osutils import has_symlinks, rename, pathjoin
26
 
from bzrlib.tests import TestCase, TestCaseInTempDir
 
24
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
 
25
    InventoryDirectory, InventoryEntry, TreeReference)
 
26
from bzrlib.osutils import (has_symlinks, rename, pathjoin, is_inside_any, 
 
27
    is_inside_or_parent_of_any)
 
28
from bzrlib.tests import TestCase, TestCaseWithTransport
 
29
from bzrlib.transform import TreeTransform
 
30
from bzrlib.uncommit import uncommit
27
31
 
28
32
 
29
33
class TestInventory(TestCase):
30
34
 
 
35
    def test_add_path(self):
 
36
 
 
37
        inv = Inventory(root_id=None)
 
38
        self.assertIs(None, inv.root)
 
39
        ie = inv.add_path("", "directory", "my-root")
 
40
        self.assertEqual("my-root", ie.file_id)
 
41
        self.assertIs(ie, inv.root)
 
42
 
31
43
    def test_is_within(self):
32
 
        from bzrlib.osutils import is_inside_any
33
44
 
34
45
        SRC_FOO_C = pathjoin('src', 'foo.c')
35
46
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
41
52
        for dirs, fn in [(['src'], 'srccontrol'),
42
53
                         (['src'], 'srccontrol/foo')]:
43
54
            self.assertFalse(is_inside_any(dirs, fn))
 
55
 
 
56
    def test_is_within_or_parent(self):
 
57
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
 
58
                         (['src'], 'src/foo.c'),
 
59
                         (['src/bar.c'], 'src'),
 
60
                         (['src/bar.c', 'bla/foo.c'], 'src'),
 
61
                         (['src'], 'src'),
 
62
                         ]:
 
63
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
44
64
            
 
65
        for dirs, fn in [(['src'], 'srccontrol'),
 
66
                         (['srccontrol/foo.c'], 'src'),
 
67
                         (['src'], 'srccontrol/foo')]:
 
68
            self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
 
69
 
45
70
    def test_ids(self):
46
71
        """Test detection of files within selected directories."""
47
72
        inv = Inventory()
58
83
        
59
84
        self.assert_('src-id' in inv)
60
85
 
61
 
 
62
 
    def test_version(self):
63
 
        """Inventory remembers the text's version."""
64
 
        inv = Inventory()
65
 
        ie = inv.add_path('foo.txt', 'file')
66
 
        ## XXX
 
86
    def test_non_directory_children(self):
 
87
        """Test path2id when a parent directory has no children"""
 
88
        inv = inventory.Inventory('tree_root')
 
89
        inv.add(inventory.InventoryFile('file-id','file', 
 
90
                                        parent_id='tree_root'))
 
91
        inv.add(inventory.InventoryLink('link-id','link', 
 
92
                                        parent_id='tree_root'))
 
93
        self.assertIs(None, inv.path2id('file/subfile'))
 
94
        self.assertIs(None, inv.path2id('link/subfile'))
 
95
 
 
96
    def test_iter_entries(self):
 
97
        inv = Inventory()
 
98
        
 
99
        for args in [('src', 'directory', 'src-id'), 
 
100
                     ('doc', 'directory', 'doc-id'), 
 
101
                     ('src/hello.c', 'file', 'hello-id'),
 
102
                     ('src/bye.c', 'file', 'bye-id'),
 
103
                     ('Makefile', 'file', 'makefile-id')]:
 
104
            inv.add_path(*args)
 
105
 
 
106
        self.assertEqual([
 
107
            ('', ROOT_ID),
 
108
            ('Makefile', 'makefile-id'),
 
109
            ('doc', 'doc-id'),
 
110
            ('src', 'src-id'),
 
111
            ('src/bye.c', 'bye-id'),
 
112
            ('src/hello.c', 'hello-id'),
 
113
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
 
114
            
 
115
    def test_iter_entries_by_dir(self):
 
116
        inv = Inventory()
 
117
        
 
118
        for args in [('src', 'directory', 'src-id'), 
 
119
                     ('doc', 'directory', 'doc-id'), 
 
120
                     ('src/hello.c', 'file', 'hello-id'),
 
121
                     ('src/bye.c', 'file', 'bye-id'),
 
122
                     ('zz', 'file', 'zz-id'),
 
123
                     ('src/sub/', 'directory', 'sub-id'),
 
124
                     ('src/zz.c', 'file', 'zzc-id'),
 
125
                     ('src/sub/a', 'file', 'a-id'),
 
126
                     ('Makefile', 'file', 'makefile-id')]:
 
127
            inv.add_path(*args)
 
128
 
 
129
        self.assertEqual([
 
130
            ('', ROOT_ID),
 
131
            ('Makefile', 'makefile-id'),
 
132
            ('doc', 'doc-id'),
 
133
            ('src', 'src-id'),
 
134
            ('zz', 'zz-id'),
 
135
            ('src/bye.c', 'bye-id'),
 
136
            ('src/hello.c', 'hello-id'),
 
137
            ('src/sub', 'sub-id'),
 
138
            ('src/zz.c', 'zzc-id'),
 
139
            ('src/sub/a', 'a-id'),
 
140
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir()])
 
141
            
 
142
        self.assertEqual([
 
143
            ('', ROOT_ID),
 
144
            ('Makefile', 'makefile-id'),
 
145
            ('doc', 'doc-id'),
 
146
            ('src', 'src-id'),
 
147
            ('zz', 'zz-id'),
 
148
            ('src/bye.c', 'bye-id'),
 
149
            ('src/hello.c', 'hello-id'),
 
150
            ('src/sub', 'sub-id'),
 
151
            ('src/zz.c', 'zzc-id'),
 
152
            ('src/sub/a', 'a-id'),
 
153
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
 
154
                specific_file_ids=('a-id', 'zzc-id', 'doc-id', ROOT_ID,
 
155
                'hello-id', 'bye-id', 'zz-id', 'src-id', 'makefile-id', 
 
156
                'sub-id'))])
 
157
 
 
158
        self.assertEqual([
 
159
            ('Makefile', 'makefile-id'),
 
160
            ('doc', 'doc-id'),
 
161
            ('zz', 'zz-id'),
 
162
            ('src/bye.c', 'bye-id'),
 
163
            ('src/hello.c', 'hello-id'),
 
164
            ('src/zz.c', 'zzc-id'),
 
165
            ('src/sub/a', 'a-id'),
 
166
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
 
167
                specific_file_ids=('a-id', 'zzc-id', 'doc-id',
 
168
                'hello-id', 'bye-id', 'zz-id', 'makefile-id'))])
 
169
 
 
170
        self.assertEqual([
 
171
            ('Makefile', 'makefile-id'),
 
172
            ('src/bye.c', 'bye-id'),
 
173
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
 
174
                specific_file_ids=('bye-id', 'makefile-id'))])
 
175
 
 
176
        self.assertEqual([
 
177
            ('Makefile', 'makefile-id'),
 
178
            ('src/bye.c', 'bye-id'),
 
179
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
 
180
                specific_file_ids=('bye-id', 'makefile-id'))])
 
181
 
 
182
        self.assertEqual([
 
183
            ('src/bye.c', 'bye-id'),
 
184
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir(
 
185
                specific_file_ids=('bye-id',))])
 
186
 
 
187
    def test_add_recursive(self):
 
188
        parent = InventoryDirectory('src-id', 'src', ROOT_ID)
 
189
        child = InventoryFile('hello-id', 'hello.c', 'src-id')
 
190
        parent.children[child.file_id] = child
 
191
        inv = Inventory()
 
192
        inv.add(parent)
 
193
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
67
194
 
68
195
 
69
196
class TestInventoryEntry(TestCase):
131
258
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
132
259
        self.failIf(link.has_text())
133
260
 
134
 
 
135
 
class TestEntryDiffing(TestCaseInTempDir):
 
261
    def test_make_entry(self):
 
262
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
 
263
            inventory.InventoryFile)
 
264
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
 
265
            inventory.InventoryLink)
 
266
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
 
267
            inventory.InventoryDirectory)
 
268
 
 
269
    def test_make_entry_non_normalized(self):
 
270
        orig_normalized_filename = osutils.normalized_filename
 
271
 
 
272
        try:
 
273
            osutils.normalized_filename = osutils._accessible_normalized_filename
 
274
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
 
275
            self.assertEqual(u'\xe5', entry.name)
 
276
            self.assertIsInstance(entry, inventory.InventoryFile)
 
277
 
 
278
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
279
            self.assertRaises(errors.InvalidNormalization,
 
280
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
 
281
        finally:
 
282
            osutils.normalized_filename = orig_normalized_filename
 
283
 
 
284
 
 
285
class TestEntryDiffing(TestCaseWithTransport):
136
286
 
137
287
    def setUp(self):
138
288
        super(TestEntryDiffing, self).setUp()
139
 
        self.branch = Branch.initialize(u'.')
140
 
        self.wt = self.branch.working_tree()
 
289
        self.wt = self.make_branch_and_tree('.')
 
290
        self.branch = self.wt.branch
141
291
        print >> open('file', 'wb'), 'foo'
142
 
        self.branch.working_tree().add(['file'], ['fileid'])
 
292
        print >> open('binfile', 'wb'), 'foo'
 
293
        self.wt.add(['file'], ['fileid'])
 
294
        self.wt.add(['binfile'], ['binfileid'])
143
295
        if has_symlinks():
144
296
            os.symlink('target1', 'symlink')
145
 
            self.branch.working_tree().add(['symlink'], ['linkid'])
 
297
            self.wt.add(['symlink'], ['linkid'])
146
298
        self.wt.commit('message_1', rev_id = '1')
147
299
        print >> open('file', 'wb'), 'bar'
 
300
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
148
301
        if has_symlinks():
149
302
            os.unlink('symlink')
150
303
            os.symlink('target2', 'symlink')
151
304
        self.tree_1 = self.branch.repository.revision_tree('1')
152
305
        self.inv_1 = self.branch.repository.get_inventory('1')
153
306
        self.file_1 = self.inv_1['fileid']
154
 
        self.tree_2 = self.branch.working_tree()
 
307
        self.file_1b = self.inv_1['binfileid']
 
308
        self.tree_2 = self.wt
 
309
        self.tree_2.lock_read()
 
310
        self.addCleanup(self.tree_2.unlock)
155
311
        self.inv_2 = self.tree_2.read_working_inventory()
156
312
        self.file_2 = self.inv_2['fileid']
 
313
        self.file_2b = self.inv_2['binfileid']
157
314
        if has_symlinks():
158
315
            self.link_1 = self.inv_1['linkid']
159
316
            self.link_2 = self.inv_2['linkid']
164
321
                          "old_label", self.tree_1,
165
322
                          "/dev/null", None, None,
166
323
                          output)
167
 
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
168
 
                                            "+++ /dev/null\t\n"
 
324
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
325
                                            "+++ /dev/null\n"
169
326
                                            "@@ -1,1 +0,0 @@\n"
170
327
                                            "-foo\n"
171
328
                                            "\n")
176
333
                          "new_label", self.tree_1,
177
334
                          "/dev/null", None, None,
178
335
                          output, reverse=True)
179
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
180
 
                                            "+++ new_label\t\n"
 
336
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
337
                                            "+++ new_label\n"
181
338
                                            "@@ -0,0 +1,1 @@\n"
182
339
                                            "+foo\n"
183
340
                                            "\n")
188
345
                          "/dev/null", self.tree_1, 
189
346
                          "new_label", self.file_2, self.tree_2,
190
347
                          output)
191
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
192
 
                                            "+++ new_label\t\n"
 
348
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
349
                                            "+++ new_label\n"
193
350
                                            "@@ -1,1 +1,1 @@\n"
194
351
                                            "-foo\n"
195
352
                                            "+bar\n"
196
353
                                            "\n")
197
354
        
 
355
    def test_file_diff_binary(self):
 
356
        output = StringIO()
 
357
        self.file_1.diff(internal_diff, 
 
358
                          "/dev/null", self.tree_1, 
 
359
                          "new_label", self.file_2b, self.tree_2,
 
360
                          output)
 
361
        self.assertEqual(output.getvalue(), 
 
362
                         "Binary files /dev/null and new_label differ\n")
198
363
    def test_link_diff_deleted(self):
199
364
        if not has_symlinks():
200
365
            return
229
394
                         "=== target changed 'target1' => 'target2'\n")
230
395
 
231
396
 
232
 
class TestSnapshot(TestCaseInTempDir):
 
397
class TestSnapshot(TestCaseWithTransport):
233
398
 
234
399
    def setUp(self):
235
400
        # for full testing we'll need a branch
239
404
        # to change, and then test merge patterns
240
405
        # with fake parent entries.
241
406
        super(TestSnapshot, self).setUp()
242
 
        self.branch = Branch.initialize(u'.')
 
407
        self.wt = self.make_branch_and_tree('.')
 
408
        self.branch = self.wt.branch
243
409
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
244
 
        self.branch.working_tree().add(['subdir', 'subdir/file'],
 
410
        self.wt.add(['subdir', 'subdir/file'],
245
411
                                       ['dirid', 'fileid'])
246
412
        if has_symlinks():
247
413
            pass
248
 
        self.wt = self.branch.working_tree()
249
414
        self.wt.commit('message_1', rev_id = '1')
250
415
        self.tree_1 = self.branch.repository.revision_tree('1')
251
416
        self.inv_1 = self.branch.repository.get_inventory('1')
252
417
        self.file_1 = self.inv_1['fileid']
253
 
        self.work_tree = self.branch.working_tree()
254
 
        self.file_active = self.work_tree.inventory['fileid']
 
418
        self.wt.lock_write()
 
419
        self.addCleanup(self.wt.unlock)
 
420
        self.file_active = self.wt.inventory['fileid']
 
421
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
255
422
 
256
423
    def test_snapshot_new_revision(self):
257
424
        # This tests that a simple commit with no parents makes a new
258
425
        # revision value in the inventory entry
259
 
        self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree, 
260
 
                                  self.branch.repository.weave_store,
261
 
                                  self.branch.get_transaction())
 
426
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
262
427
        # expected outcome - file_1 has a revision id of '2', and we can get
263
428
        # its text of 'file contents' out of the weave.
264
429
        self.assertEqual(self.file_1.revision, '1')
265
430
        self.assertEqual(self.file_active.revision, '2')
266
431
        # this should be a separate test probably, but lets check it once..
267
 
        lines = self.branch.repository.weave_store.get_lines('fileid','2',
268
 
            self.branch.get_transaction())
 
432
        lines = self.branch.repository.weave_store.get_weave(
 
433
            'fileid', 
 
434
            self.branch.get_transaction()).get_lines('2')
269
435
        self.assertEqual(lines, ['contents of subdir/file\n'])
270
436
 
271
437
    def test_snapshot_unchanged(self):
272
438
        #This tests that a simple commit does not make a new entry for
273
439
        # an unchanged inventory entry
274
440
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
275
 
                                  self.work_tree, 
276
 
                                  self.branch.repository.weave_store,
277
 
                                  self.branch.get_transaction())
 
441
                                  self.wt, self.builder)
278
442
        self.assertEqual(self.file_1.revision, '1')
279
443
        self.assertEqual(self.file_active.revision, '1')
280
 
        self.assertRaises(errors.WeaveError,
281
 
                          self.branch.repository.weave_store.get_lines, 
282
 
                          'fileid', '2', self.branch.get_transaction())
 
444
        vf = self.branch.repository.weave_store.get_weave(
 
445
            'fileid', 
 
446
            self.branch.repository.get_transaction())
 
447
        self.assertRaises(errors.RevisionNotPresent,
 
448
                          vf.get_lines,
 
449
                          '2')
283
450
 
284
451
    def test_snapshot_merge_identical_different_revid(self):
285
452
        # This tests that a commit with two identical parents, one of which has
293
460
        self.assertEqual(self.file_1, other_ie)
294
461
        other_ie.revision = 'other'
295
462
        self.assertNotEqual(self.file_1, other_ie)
296
 
        self.branch.repository.weave_store.add_identical_text('fileid', '1', 
297
 
            'other', ['1'], self.branch.get_transaction())
 
463
        versionfile = self.branch.repository.weave_store.get_weave(
 
464
            'fileid', self.branch.repository.get_transaction())
 
465
        versionfile.clone_text('other', '1', ['1'])
298
466
        self.file_active.snapshot('2', 'subdir/file', 
299
467
                                  {'1':self.file_1, 'other':other_ie},
300
 
                                  self.work_tree, 
301
 
                                  self.branch.repository.weave_store,
302
 
                                  self.branch.get_transaction())
 
468
                                  self.wt, self.builder)
303
469
        self.assertEqual(self.file_active.revision, '2')
304
470
 
305
471
    def test_snapshot_changed(self):
306
472
        # This tests that a commit with one different parent results in a new
307
473
        # revision id in the entry.
308
 
        self.file_active.name='newname'
309
 
        rename('subdir/file', 'subdir/newname')
 
474
        self.wt.rename_one('subdir/file', 'subdir/newname')
 
475
        self.file_active = self.wt.inventory['fileid']
310
476
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
311
 
                                  self.work_tree, 
312
 
                                  self.branch.repository.weave_store,
313
 
                                  self.branch.get_transaction())
 
477
                                  self.wt, self.builder)
314
478
        # expected outcome - file_1 has a revision id of '2'
315
479
        self.assertEqual(self.file_active.revision, '2')
316
480
 
317
481
 
318
 
class TestPreviousHeads(TestCaseInTempDir):
 
482
class TestPreviousHeads(TestCaseWithTransport):
319
483
 
320
484
    def setUp(self):
321
485
        # we want several inventories, that respectively
327
491
        # D) fileid present in two inventories and one is
328
492
        #   a descendent of the other. (B, D)
329
493
        super(TestPreviousHeads, self).setUp()
 
494
        self.wt = self.make_branch_and_tree('.')
 
495
        self.branch = self.wt.branch
330
496
        self.build_tree(['file'])
331
 
        self.branch = Branch.initialize(u'.')
332
 
        self.wt = self.branch.working_tree()
333
497
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
334
498
        self.inv_A = self.branch.repository.get_inventory('A')
335
499
        self.wt.add(['file'], ['fileid'])
336
500
        self.wt.commit('add file', rev_id='B')
337
501
        self.inv_B = self.branch.repository.get_inventory('B')
338
 
        self.branch.lock_write()
339
 
        try:
340
 
            self.branch.control_files.put_utf8('revision-history', 'A\n')
341
 
        finally:
342
 
            self.branch.unlock()
 
502
        uncommit(self.branch, tree=self.wt)
343
503
        self.assertEqual(self.branch.revision_history(), ['A'])
344
504
        self.wt.commit('another add of file', rev_id='C')
345
505
        self.inv_C = self.branch.repository.get_inventory('C')
346
 
        self.wt.add_pending_merge('B')
 
506
        self.wt.add_parent_tree_id('B')
347
507
        self.wt.commit('merge in B', rev_id='D')
348
508
        self.inv_D = self.branch.repository.get_inventory('D')
349
 
        self.file_active = self.branch.working_tree().inventory['fileid']
 
509
        self.wt.lock_read()
 
510
        self.addCleanup(self.wt.unlock)
 
511
        self.file_active = self.wt.inventory['fileid']
350
512
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
351
 
            self.branch.get_transaction())
 
513
            self.branch.repository.get_transaction())
352
514
        
353
515
    def get_previous_heads(self, inventories):
354
 
        return self.file_active.find_previous_heads(inventories, self.weave)
 
516
        return self.file_active.find_previous_heads(
 
517
            inventories, 
 
518
            self.branch.repository.weave_store,
 
519
            self.branch.repository.get_transaction())
355
520
        
356
521
    def test_fileid_in_no_inventory(self):
357
522
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
381
546
    # TODO: test two inventories with the same file revision 
382
547
 
383
548
 
384
 
class TestExecutable(TestCaseInTempDir):
385
 
 
386
 
    def test_stays_executable(self):
387
 
        basic_inv = """<inventory format="5">
388
 
<file file_id="a-20051208024829-849e76f7968d7a86" name="a" executable="yes" />
389
 
<file file_id="b-20051208024829-849e76f7968d7a86" name="b" />
390
 
</inventory>
391
 
"""
392
 
        os.mkdir('b1')
393
 
        b = Branch.initialize('b1')
 
549
class TestDescribeChanges(TestCase):
 
550
 
 
551
    def test_describe_change(self):
 
552
        # we need to test the following change combinations:
 
553
        # rename
 
554
        # reparent
 
555
        # modify
 
556
        # gone
 
557
        # added
 
558
        # renamed/reparented and modified
 
559
        # change kind (perhaps can't be done yet?)
 
560
        # also, merged in combination with all of these?
 
561
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
562
        old_a.text_sha1 = '123132'
 
563
        old_a.text_size = 0
 
564
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
565
        new_a.text_sha1 = '123132'
 
566
        new_a.text_size = 0
 
567
 
 
568
        self.assertChangeDescription('unchanged', old_a, new_a)
 
569
 
 
570
        new_a.text_size = 10
 
571
        new_a.text_sha1 = 'abcabc'
 
572
        self.assertChangeDescription('modified', old_a, new_a)
 
573
 
 
574
        self.assertChangeDescription('added', None, new_a)
 
575
        self.assertChangeDescription('removed', old_a, None)
 
576
        # perhaps a bit questionable but seems like the most reasonable thing...
 
577
        self.assertChangeDescription('unchanged', None, None)
 
578
 
 
579
        # in this case it's both renamed and modified; show a rename and 
 
580
        # modification:
 
581
        new_a.name = 'newfilename'
 
582
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
583
 
 
584
        # reparenting is 'renaming'
 
585
        new_a.name = old_a.name
 
586
        new_a.parent_id = 'somedir-id'
 
587
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
588
 
 
589
        # reset the content values so its not modified
 
590
        new_a.text_size = old_a.text_size
 
591
        new_a.text_sha1 = old_a.text_sha1
 
592
        new_a.name = old_a.name
 
593
 
 
594
        new_a.name = 'newfilename'
 
595
        self.assertChangeDescription('renamed', old_a, new_a)
 
596
 
 
597
        # reparenting is 'renaming'
 
598
        new_a.name = old_a.name
 
599
        new_a.parent_id = 'somedir-id'
 
600
        self.assertChangeDescription('renamed', old_a, new_a)
 
601
 
 
602
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
603
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
604
        self.assertEqual(expected_change, change)
 
605
 
 
606
 
 
607
class TestRevert(TestCaseWithTransport):
 
608
 
 
609
    def test_dangling_id(self):
 
610
        wt = self.make_branch_and_tree('b1')
 
611
        wt.lock_tree_write()
 
612
        self.addCleanup(wt.unlock)
 
613
        self.assertEqual(len(wt.inventory), 1)
394
614
        open('b1/a', 'wb').write('a test\n')
395
 
        open('b1/b', 'wb').write('b test\n')
396
 
        os.chmod('b1/a', 0755)
397
 
        os.chmod('b1/b', 0644)
398
 
        # Manually writing the inventory, to ensure that
399
 
        # the executable="yes" entry is set for 'a' and not for 'b'
400
 
        open('b1/.bzr/inventory', 'wb').write(basic_inv)
401
 
 
402
 
        a_id = "a-20051208024829-849e76f7968d7a86"
403
 
        b_id = "b-20051208024829-849e76f7968d7a86"
404
 
        t = b.working_tree()
405
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
406
 
 
407
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
408
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
409
 
 
410
 
        t.commit('adding a,b', rev_id='r1')
411
 
 
412
 
        rev_tree = b.repository.revision_tree('r1')
413
 
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
414
 
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
415
 
 
416
 
        self.failUnless(rev_tree.inventory[a_id].executable)
417
 
        self.failIf(rev_tree.inventory[b_id].executable)
418
 
 
419
 
        # Make sure the entries are gone
420
 
        os.remove('b1/a')
421
 
        os.remove('b1/b')
422
 
        self.failIf(t.has_id(a_id))
423
 
        self.failIf(t.has_filename('a'))
424
 
        self.failIf(t.has_id(b_id))
425
 
        self.failIf(t.has_filename('b'))
426
 
 
427
 
        # Make sure that revert is able to bring them back,
428
 
        # and sets 'a' back to being executable
429
 
 
430
 
        t.revert(['a', 'b'], rev_tree, backups=False)
431
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
432
 
 
433
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
434
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
435
 
 
436
 
        # Now remove them again, and make sure that after a
437
 
        # commit, they are still marked correctly
438
 
        os.remove('b1/a')
439
 
        os.remove('b1/b')
440
 
        t.commit('removed', rev_id='r2')
441
 
 
442
 
        self.assertEqual([], [cn for cn,ie in t.inventory.iter_entries()])
443
 
        self.failIf(t.has_id(a_id))
444
 
        self.failIf(t.has_filename('a'))
445
 
        self.failIf(t.has_id(b_id))
446
 
        self.failIf(t.has_filename('b'))
447
 
 
448
 
        # Now revert back to the previous commit
449
 
        t.revert([], rev_tree, backups=False)
450
 
        # TODO: FIXME: For some reason, after revert, the tree does not 
451
 
        # regenerate its working inventory, so we have to manually delete
452
 
        # the working tree, and create a new one
453
 
        # This seems to happen any time you do a merge operation on the
454
 
        # working tree
455
 
        del t
456
 
        t = b.working_tree()
457
 
 
458
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
459
 
 
460
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
461
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
462
 
 
463
 
        # Now make sure that 'bzr branch' also preserves the
464
 
        # executable bit
465
 
        # TODO: Maybe this should be a blackbox test
466
 
        b.clone('b2', revision='r1')
467
 
        b2 = Branch.open('b2')
468
 
        self.assertEquals('r1', b2.last_revision())
469
 
        t2 = b2.working_tree()
470
 
 
471
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
472
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
473
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
474
 
 
475
 
        # Make sure pull will delete the files
476
 
        t2.pull(b)
477
 
        self.assertEquals('r2', b2.last_revision())
478
 
        # FIXME: Same thing here, t2 needs to be recreated
479
 
        del t2
480
 
        t2 = b2.working_tree()
481
 
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
482
 
 
483
 
        # Now commit the changes on the first branch
484
 
        # so that the second branch can pull the changes
485
 
        # and make sure that the executable bit has been copied
486
 
        t.commit('resurrected', rev_id='r3')
487
 
 
488
 
        t2.pull(b)
489
 
        # FIXME: And here
490
 
        del t2
491
 
        t2 = b2.working_tree()
492
 
        self.assertEquals('r3', b2.last_revision())
493
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
494
 
 
495
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
496
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
 
615
        wt.add('a')
 
616
        self.assertEqual(len(wt.inventory), 2)
 
617
        wt.flush() # workaround revert doing wt._write_inventory for now.
 
618
        os.unlink('b1/a')
 
619
        wt.revert([])
 
620
        self.assertEqual(len(wt.inventory), 1)
 
621
 
 
622
 
 
623
class TestIsRoot(TestCase):
 
624
    """Ensure our root-checking code is accurate."""
 
625
 
 
626
    def test_is_root(self):
 
627
        inv = Inventory('TREE_ROOT')
 
628
        self.assertTrue(inv.is_root('TREE_ROOT'))
 
629
        self.assertFalse(inv.is_root('booga'))
 
630
        inv.root.file_id = 'booga'
 
631
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
632
        self.assertTrue(inv.is_root('booga'))
 
633
        # works properly even if no root is set
 
634
        inv.root = None
 
635
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
636
        self.assertFalse(inv.is_root('booga'))
 
637
 
 
638
 
 
639
class TestTreeReference(TestCase):
 
640
    
 
641
    def test_create(self):
 
642
        inv = Inventory('tree-root-123')
 
643
        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
 
644
                              revision='rev', reference_revision='rev2'))