1
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005, 2006 Canonical Ltd
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.
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.
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
17
17
from cStringIO import StringIO
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
44
52
for dirs, fn in [(['src'], 'srccontrol'),
45
53
(['src'], 'srccontrol/foo')]:
46
54
self.assertFalse(is_inside_any(dirs, fn))
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'),
63
self.assert_(is_inside_or_parent_of_any(dirs, fn))
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))
48
70
def test_ids(self):
49
71
"""Test detection of files within selected directories."""
79
112
('src/hello.c', 'hello-id'),
80
113
], [(path, ie.file_id) for path, ie in inv.iter_entries()])
82
def test_version(self):
83
"""Inventory remembers the text's version."""
85
ie = inv.add_path('foo.txt', 'file')
115
def test_iter_entries_by_dir(self):
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')]:
131
('Makefile', 'makefile-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()])
144
('Makefile', 'makefile-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',
159
('Makefile', 'makefile-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'))])
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'))])
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'))])
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',))])
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
193
self.assertEqual('src/hello.c', inv.id2path('hello-id'))
89
196
class TestInventoryEntry(TestCase):
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']
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')
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')
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},
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')
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},
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')
482
604
self.assertEqual(expected_change, change)
485
class TestExecutable(TestCaseWithTransport):
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')
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)
497
self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
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()])
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")
506
wt.commit('adding a,b', rev_id='r1')
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")
512
self.failUnless(rev_tree.inventory[a_id].executable)
513
self.failIf(rev_tree.inventory[b_id].executable)
515
# Make sure the entries are gone
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'))
523
# Make sure that revert is able to bring them back,
524
# and sets 'a' back to being executable
526
wt.revert(['a', 'b'], rev_tree, backups=False)
527
self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
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")
532
# Now remove them again, and make sure that after a
533
# commit, they are still marked correctly
536
wt.commit('removed', rev_id='r2')
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'))
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()])
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")
551
# Now make sure that 'bzr branch' also preserves the
553
# TODO: Maybe this should be a blackbox test
554
d2 = b.bzrdir.clone('b2', revision_id='r1')
555
t2 = d2.open_workingtree()
557
self.assertEquals('r1', b2.last_revision())
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")
563
# Make sure pull will delete the files
565
self.assertEquals('r2', b2.last_revision())
566
self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
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')
574
self.assertEquals('r3', b2.last_revision())
575
self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
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")
581
607
class TestRevert(TestCaseWithTransport):
583
609
def test_dangling_id(self):
584
610
wt = self.make_branch_and_tree('b1')
612
self.addCleanup(wt.unlock)
585
613
self.assertEqual(len(wt.inventory), 1)
586
614
open('b1/a', 'wb').write('a test\n')
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')
591
620
self.assertEqual(len(wt.inventory), 1)
623
class TestIsRoot(TestCase):
624
"""Ensure our root-checking code is accurate."""
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
635
self.assertFalse(inv.is_root('TREE_ROOT'))
636
self.assertFalse(inv.is_root('booga'))
639
class TestTreeReference(TestCase):
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'))