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."""
62
84
self.assert_('src-id' in inv)
64
def test_version(self):
65
"""Inventory remembers the text's version."""
67
ie = inv.add_path('foo.txt', 'file')
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'))
96
def test_iter_entries(self):
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')]:
108
('Makefile', 'makefile-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()])
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'))
71
196
class TestInventoryEntry(TestCase):
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']
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')
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')
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},
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')
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},
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')
464
604
self.assertEqual(expected_change, change)
467
class TestExecutable(TestCaseWithTransport):
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')
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)
479
self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
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()])
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")
488
wt.commit('adding a,b', rev_id='r1')
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")
494
self.failUnless(rev_tree.inventory[a_id].executable)
495
self.failIf(rev_tree.inventory[b_id].executable)
497
# Make sure the entries are gone
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'))
505
# Make sure that revert is able to bring them back,
506
# and sets 'a' back to being executable
508
wt.revert(['a', 'b'], rev_tree, backups=False)
509
self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
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")
514
# Now remove them again, and make sure that after a
515
# commit, they are still marked correctly
518
wt.commit('removed', rev_id='r2')
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'))
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()])
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")
533
# Now make sure that 'bzr branch' also preserves the
535
# TODO: Maybe this should be a blackbox test
536
d2 = b.bzrdir.clone('b2', revision_id='r1')
537
t2 = d2.open_workingtree()
539
self.assertEquals('r1', b2.last_revision())
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")
545
# Make sure pull will delete the files
547
self.assertEquals('r2', b2.last_revision())
548
self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
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')
556
self.assertEquals('r3', b2.last_revision())
557
self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
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")
563
607
class TestRevert(TestCaseWithTransport):
565
609
def test_dangling_id(self):
566
610
wt = self.make_branch_and_tree('b1')
612
self.addCleanup(wt.unlock)
567
613
self.assertEqual(len(wt.inventory), 1)
568
614
open('b1/a', 'wb').write('a test\n')
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')
573
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'))