~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Robert Collins
  • Date: 2007-03-08 04:06:06 UTC
  • mfrom: (2323.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 2442.
  • Revision ID: robertc@robertcollins.net-20070308040606-84gsniv56huiyjt4
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
 
 
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
 
64
96
    def test_iter_entries(self):
65
97
        inv = Inventory()
66
98
        
72
104
            inv.add_path(*args)
73
105
 
74
106
        self.assertEqual([
 
107
            ('', ROOT_ID),
75
108
            ('Makefile', 'makefile-id'),
76
109
            ('doc', 'doc-id'),
77
110
            ('src', 'src-id'),
79
112
            ('src/hello.c', 'hello-id'),
80
113
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
81
114
            
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
 
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'))
87
194
 
88
195
 
89
196
class TestInventoryEntry(TestCase):
159
266
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
160
267
            inventory.InventoryDirectory)
161
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
 
162
285
class TestEntryDiffing(TestCaseWithTransport):
163
286
 
164
287
    def setUp(self):
183
306
        self.file_1 = self.inv_1['fileid']
184
307
        self.file_1b = self.inv_1['binfileid']
185
308
        self.tree_2 = self.wt
 
309
        self.tree_2.lock_read()
 
310
        self.addCleanup(self.tree_2.unlock)
186
311
        self.inv_2 = self.tree_2.read_working_inventory()
187
312
        self.file_2 = self.inv_2['fileid']
188
313
        self.file_2b = self.inv_2['binfileid']
196
321
                          "old_label", self.tree_1,
197
322
                          "/dev/null", None, None,
198
323
                          output)
199
 
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
200
 
                                            "+++ /dev/null\t\n"
 
324
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
325
                                            "+++ /dev/null\n"
201
326
                                            "@@ -1,1 +0,0 @@\n"
202
327
                                            "-foo\n"
203
328
                                            "\n")
208
333
                          "new_label", self.tree_1,
209
334
                          "/dev/null", None, None,
210
335
                          output, reverse=True)
211
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
212
 
                                            "+++ new_label\t\n"
 
336
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
337
                                            "+++ new_label\n"
213
338
                                            "@@ -0,0 +1,1 @@\n"
214
339
                                            "+foo\n"
215
340
                                            "\n")
220
345
                          "/dev/null", self.tree_1, 
221
346
                          "new_label", self.file_2, self.tree_2,
222
347
                          output)
223
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
224
 
                                            "+++ new_label\t\n"
 
348
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
349
                                            "+++ new_label\n"
225
350
                                            "@@ -1,1 +1,1 @@\n"
226
351
                                            "-foo\n"
227
352
                                            "+bar\n"
290
415
        self.tree_1 = self.branch.repository.revision_tree('1')
291
416
        self.inv_1 = self.branch.repository.get_inventory('1')
292
417
        self.file_1 = self.inv_1['fileid']
 
418
        self.wt.lock_write()
 
419
        self.addCleanup(self.wt.unlock)
293
420
        self.file_active = self.wt.inventory['fileid']
 
421
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
294
422
 
295
423
    def test_snapshot_new_revision(self):
296
424
        # This tests that a simple commit with no parents makes a new
297
425
        # 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())
 
426
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
301
427
        # expected outcome - file_1 has a revision id of '2', and we can get
302
428
        # its text of 'file contents' out of the weave.
303
429
        self.assertEqual(self.file_1.revision, '1')
312
438
        #This tests that a simple commit does not make a new entry for
313
439
        # an unchanged inventory entry
314
440
        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())
 
441
                                  self.wt, self.builder)
318
442
        self.assertEqual(self.file_1.revision, '1')
319
443
        self.assertEqual(self.file_active.revision, '1')
320
444
        vf = self.branch.repository.weave_store.get_weave(
341
465
        versionfile.clone_text('other', '1', ['1'])
342
466
        self.file_active.snapshot('2', 'subdir/file', 
343
467
                                  {'1':self.file_1, 'other':other_ie},
344
 
                                  self.wt, 
345
 
                                  self.branch.repository.weave_store,
346
 
                                  self.branch.get_transaction())
 
468
                                  self.wt, self.builder)
347
469
        self.assertEqual(self.file_active.revision, '2')
348
470
 
349
471
    def test_snapshot_changed(self):
350
472
        # This tests that a commit with one different parent results in a new
351
473
        # revision id in the entry.
352
 
        self.file_active.name='newname'
353
 
        rename('subdir/file', 'subdir/newname')
 
474
        self.wt.rename_one('subdir/file', 'subdir/newname')
 
475
        self.file_active = self.wt.inventory['fileid']
354
476
        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())
 
477
                                  self.wt, self.builder)
358
478
        # expected outcome - file_1 has a revision id of '2'
359
479
        self.assertEqual(self.file_active.revision, '2')
360
480
 
383
503
        self.assertEqual(self.branch.revision_history(), ['A'])
384
504
        self.wt.commit('another add of file', rev_id='C')
385
505
        self.inv_C = self.branch.repository.get_inventory('C')
386
 
        self.wt.add_pending_merge('B')
 
506
        self.wt.add_parent_tree_id('B')
387
507
        self.wt.commit('merge in B', rev_id='D')
388
508
        self.inv_D = self.branch.repository.get_inventory('D')
 
509
        self.wt.lock_read()
 
510
        self.addCleanup(self.wt.unlock)
389
511
        self.file_active = self.wt.inventory['fileid']
390
512
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
391
513
            self.branch.repository.get_transaction())
482
604
        self.assertEqual(expected_change, change)
483
605
 
484
606
 
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
607
class TestRevert(TestCaseWithTransport):
582
608
 
583
609
    def test_dangling_id(self):
584
610
        wt = self.make_branch_and_tree('b1')
 
611
        wt.lock_tree_write()
 
612
        self.addCleanup(wt.unlock)
585
613
        self.assertEqual(len(wt.inventory), 1)
586
614
        open('b1/a', 'wb').write('a test\n')
587
615
        wt.add('a')
588
616
        self.assertEqual(len(wt.inventory), 2)
 
617
        wt.flush() # workaround revert doing wt._write_inventory for now.
589
618
        os.unlink('b1/a')
590
619
        wt.revert([])
591
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'))