~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

Merge bzr.dev

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
24
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
24
 
    InventoryDirectory, InventoryEntry)
25
 
import bzrlib.inventory as inventory
26
 
from bzrlib.osutils import has_symlinks, rename, pathjoin
 
25
    InventoryDirectory, InventoryEntry, TreeReference)
 
26
from bzrlib.osutils import (has_symlinks, rename, pathjoin, is_inside_any, 
 
27
    is_inside_or_parent_of_any)
27
28
from bzrlib.tests import TestCase, TestCaseWithTransport
28
29
from bzrlib.transform import TreeTransform
29
30
from bzrlib.uncommit import uncommit
31
32
 
32
33
class TestInventory(TestCase):
33
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
 
34
43
    def test_is_within(self):
35
 
        from bzrlib.osutils import is_inside_any
36
44
 
37
45
        SRC_FOO_C = pathjoin('src', 'foo.c')
38
46
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
44
52
        for dirs, fn in [(['src'], 'srccontrol'),
45
53
                         (['src'], 'srccontrol/foo')]:
46
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))
47
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
 
48
70
    def test_ids(self):
49
71
        """Test detection of files within selected directories."""
50
72
        inv = Inventory()
61
83
        
62
84
        self.assert_('src-id' in inv)
63
85
 
64
 
    def test_version(self):
65
 
        """Inventory remembers the text's version."""
66
 
        inv = Inventory()
67
 
        ie = inv.add_path('foo.txt', 'file')
68
 
        ## 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'))
69
194
 
70
195
 
71
196
class TestInventoryEntry(TestCase):
141
266
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
142
267
            inventory.InventoryDirectory)
143
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
 
144
285
class TestEntryDiffing(TestCaseWithTransport):
145
286
 
146
287
    def setUp(self):
165
306
        self.file_1 = self.inv_1['fileid']
166
307
        self.file_1b = self.inv_1['binfileid']
167
308
        self.tree_2 = self.wt
 
309
        self.tree_2.lock_read()
 
310
        self.addCleanup(self.tree_2.unlock)
168
311
        self.inv_2 = self.tree_2.read_working_inventory()
169
312
        self.file_2 = self.inv_2['fileid']
170
313
        self.file_2b = self.inv_2['binfileid']
178
321
                          "old_label", self.tree_1,
179
322
                          "/dev/null", None, None,
180
323
                          output)
181
 
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
182
 
                                            "+++ /dev/null\t\n"
 
324
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
325
                                            "+++ /dev/null\n"
183
326
                                            "@@ -1,1 +0,0 @@\n"
184
327
                                            "-foo\n"
185
328
                                            "\n")
190
333
                          "new_label", self.tree_1,
191
334
                          "/dev/null", None, None,
192
335
                          output, reverse=True)
193
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
194
 
                                            "+++ new_label\t\n"
 
336
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
337
                                            "+++ new_label\n"
195
338
                                            "@@ -0,0 +1,1 @@\n"
196
339
                                            "+foo\n"
197
340
                                            "\n")
202
345
                          "/dev/null", self.tree_1, 
203
346
                          "new_label", self.file_2, self.tree_2,
204
347
                          output)
205
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
206
 
                                            "+++ new_label\t\n"
 
348
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
349
                                            "+++ new_label\n"
207
350
                                            "@@ -1,1 +1,1 @@\n"
208
351
                                            "-foo\n"
209
352
                                            "+bar\n"
272
415
        self.tree_1 = self.branch.repository.revision_tree('1')
273
416
        self.inv_1 = self.branch.repository.get_inventory('1')
274
417
        self.file_1 = self.inv_1['fileid']
 
418
        self.wt.lock_write()
 
419
        self.addCleanup(self.wt.unlock)
275
420
        self.file_active = self.wt.inventory['fileid']
 
421
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
276
422
 
277
423
    def test_snapshot_new_revision(self):
278
424
        # This tests that a simple commit with no parents makes a new
279
425
        # revision value in the inventory entry
280
 
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, 
281
 
                                  self.branch.repository.weave_store,
282
 
                                  self.branch.get_transaction())
 
426
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
283
427
        # expected outcome - file_1 has a revision id of '2', and we can get
284
428
        # its text of 'file contents' out of the weave.
285
429
        self.assertEqual(self.file_1.revision, '1')
294
438
        #This tests that a simple commit does not make a new entry for
295
439
        # an unchanged inventory entry
296
440
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
297
 
                                  self.wt, 
298
 
                                  self.branch.repository.weave_store,
299
 
                                  self.branch.get_transaction())
 
441
                                  self.wt, self.builder)
300
442
        self.assertEqual(self.file_1.revision, '1')
301
443
        self.assertEqual(self.file_active.revision, '1')
302
444
        vf = self.branch.repository.weave_store.get_weave(
323
465
        versionfile.clone_text('other', '1', ['1'])
324
466
        self.file_active.snapshot('2', 'subdir/file', 
325
467
                                  {'1':self.file_1, 'other':other_ie},
326
 
                                  self.wt, 
327
 
                                  self.branch.repository.weave_store,
328
 
                                  self.branch.get_transaction())
 
468
                                  self.wt, self.builder)
329
469
        self.assertEqual(self.file_active.revision, '2')
330
470
 
331
471
    def test_snapshot_changed(self):
332
472
        # This tests that a commit with one different parent results in a new
333
473
        # revision id in the entry.
334
 
        self.file_active.name='newname'
335
 
        rename('subdir/file', 'subdir/newname')
 
474
        self.wt.rename_one('subdir/file', 'subdir/newname')
 
475
        self.file_active = self.wt.inventory['fileid']
336
476
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
337
 
                                  self.wt,
338
 
                                  self.branch.repository.weave_store,
339
 
                                  self.branch.get_transaction())
 
477
                                  self.wt, self.builder)
340
478
        # expected outcome - file_1 has a revision id of '2'
341
479
        self.assertEqual(self.file_active.revision, '2')
342
480
 
365
503
        self.assertEqual(self.branch.revision_history(), ['A'])
366
504
        self.wt.commit('another add of file', rev_id='C')
367
505
        self.inv_C = self.branch.repository.get_inventory('C')
368
 
        self.wt.add_pending_merge('B')
 
506
        self.wt.add_parent_tree_id('B')
369
507
        self.wt.commit('merge in B', rev_id='D')
370
508
        self.inv_D = self.branch.repository.get_inventory('D')
 
509
        self.wt.lock_read()
 
510
        self.addCleanup(self.wt.unlock)
371
511
        self.file_active = self.wt.inventory['fileid']
372
512
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
373
513
            self.branch.repository.get_transaction())
464
604
        self.assertEqual(expected_change, change)
465
605
 
466
606
 
467
 
class TestExecutable(TestCaseWithTransport):
468
 
 
469
 
    def test_stays_executable(self):
470
 
        a_id = "a-20051208024829-849e76f7968d7a86"
471
 
        b_id = "b-20051208024829-849e76f7968d7a86"
472
 
        wt = self.make_branch_and_tree('b1')
473
 
        b = wt.branch
474
 
        tt = TreeTransform(wt)
475
 
        tt.new_file('a', tt.root, 'a test\n', a_id, True)
476
 
        tt.new_file('b', tt.root, 'b test\n', b_id, False)
477
 
        tt.apply()
478
 
 
479
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
480
 
 
481
 
        # reopen the tree and ensure it stuck.
482
 
        wt = wt.bzrdir.open_workingtree()
483
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
484
 
 
485
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
486
 
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
487
 
 
488
 
        wt.commit('adding a,b', rev_id='r1')
489
 
 
490
 
        rev_tree = b.repository.revision_tree('r1')
491
 
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
492
 
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
493
 
 
494
 
        self.failUnless(rev_tree.inventory[a_id].executable)
495
 
        self.failIf(rev_tree.inventory[b_id].executable)
496
 
 
497
 
        # Make sure the entries are gone
498
 
        os.remove('b1/a')
499
 
        os.remove('b1/b')
500
 
        self.failIf(wt.has_id(a_id))
501
 
        self.failIf(wt.has_filename('a'))
502
 
        self.failIf(wt.has_id(b_id))
503
 
        self.failIf(wt.has_filename('b'))
504
 
 
505
 
        # Make sure that revert is able to bring them back,
506
 
        # and sets 'a' back to being executable
507
 
 
508
 
        wt.revert(['a', 'b'], rev_tree, backups=False)
509
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
510
 
 
511
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
512
 
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
513
 
 
514
 
        # Now remove them again, and make sure that after a
515
 
        # commit, they are still marked correctly
516
 
        os.remove('b1/a')
517
 
        os.remove('b1/b')
518
 
        wt.commit('removed', rev_id='r2')
519
 
 
520
 
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
521
 
        self.failIf(wt.has_id(a_id))
522
 
        self.failIf(wt.has_filename('a'))
523
 
        self.failIf(wt.has_id(b_id))
524
 
        self.failIf(wt.has_filename('b'))
525
 
 
526
 
        # Now revert back to the previous commit
527
 
        wt.revert([], rev_tree, backups=False)
528
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
529
 
 
530
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
531
 
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
532
 
 
533
 
        # Now make sure that 'bzr branch' also preserves the
534
 
        # executable bit
535
 
        # TODO: Maybe this should be a blackbox test
536
 
        d2 = b.bzrdir.clone('b2', revision_id='r1')
537
 
        t2 = d2.open_workingtree()
538
 
        b2 = t2.branch
539
 
        self.assertEquals('r1', b2.last_revision())
540
 
 
541
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
542
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
543
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
544
 
 
545
 
        # Make sure pull will delete the files
546
 
        t2.pull(b)
547
 
        self.assertEquals('r2', b2.last_revision())
548
 
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
549
 
 
550
 
        # Now commit the changes on the first branch
551
 
        # so that the second branch can pull the changes
552
 
        # and make sure that the executable bit has been copied
553
 
        wt.commit('resurrected', rev_id='r3')
554
 
 
555
 
        t2.pull(b)
556
 
        self.assertEquals('r3', b2.last_revision())
557
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
558
 
 
559
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
560
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
561
 
 
562
 
 
563
607
class TestRevert(TestCaseWithTransport):
564
608
 
565
609
    def test_dangling_id(self):
566
610
        wt = self.make_branch_and_tree('b1')
 
611
        wt.lock_tree_write()
 
612
        self.addCleanup(wt.unlock)
567
613
        self.assertEqual(len(wt.inventory), 1)
568
614
        open('b1/a', 'wb').write('a test\n')
569
615
        wt.add('a')
570
616
        self.assertEqual(len(wt.inventory), 2)
 
617
        wt.flush() # workaround revert doing wt._write_inventory for now.
571
618
        os.unlink('b1/a')
572
619
        wt.revert([])
573
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'))