1
# Copyright (C) 2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Test symlink support.
28
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
31
class TestSmartAddTree(TestCaseWithWorkingTree):
33
# See eg <https://bugs.launchpad.net/bzr/+bug/192859>
35
_test_needs_features = [tests.SymlinkFeature]
37
def test_smart_add_symlink(self):
38
tree = self.make_branch_and_tree('tree')
39
self.build_tree_contents([
40
('tree/link@', 'target'),
42
tree.smart_add(['tree/link'])
43
self.assertIsNot(None, tree.path2id('link'))
44
self.assertIs(None, tree.path2id('target'))
45
self.assertEqual('symlink',
46
tree.kind(tree.path2id('link')))
48
def test_smart_add_symlink_pointing_outside(self):
49
tree = self.make_branch_and_tree('tree')
50
self.build_tree_contents([
51
('tree/link@', '../../../../target'),
53
tree.smart_add(['tree/link'])
54
self.assertIsNot(None, tree.path2id('link'))
55
self.assertIs(None, tree.path2id('target'))
56
self.assertEqual('symlink',
57
tree.kind(tree.path2id('link')))
59
def test_add_file_under_symlink(self):
61
# https://bugs.launchpad.net/bzr/+bug/192859/comments/3
62
tree = self.make_branch_and_tree('tree')
63
self.build_tree_contents([
64
('tree/link@', 'dir'),
66
('tree/dir/file', 'content'),
69
tree.smart_add(['tree/link/file']),
70
([u'dir', u'dir/file'], {}))
71
# should add the actual parent directory, not the apparent parent
72
# (which is actually a symlink)
73
self.assertTrue(tree.path2id('dir/file'))
74
self.assertTrue(tree.path2id('dir'))
75
self.assertIs(None, tree.path2id('link'))
76
self.assertIs(None, tree.path2id('link/file'))
79
class TestKindChanges(TestCaseWithWorkingTree):
81
_test_needs_features = [tests.SymlinkFeature]
83
def test_symlink_changes_to_dir(self):
84
# <https://bugs.launchpad.net/bzr/+bug/192859>:
85
# we had some past problems with the workingtree remembering for too
86
# long what kind of object was at a particular name; we really
87
# shouldn't do that. Operating on the dirstate through passing
88
# inventory deltas rather than mutating the inventory largely avoids
90
tree = self.make_branch_and_tree('tree')
91
self.build_tree_contents([
92
('tree/a@', 'target')])
93
tree.smart_add(['tree/a'])
94
tree.commit('add symlink')
96
self.build_tree_contents([
98
('tree/a/f', 'content'),
100
tree.smart_add(['tree/a/f'])
101
tree.commit('change to dir')
103
self.addCleanup(tree.unlock)
104
self.assertEquals([], list(tree.iter_changes(tree.basis_tree())))
106
['a', 'a/f'], sorted(info[0] for info in tree.list_files()))
108
def test_dir_changes_to_symlink(self):
109
# <https://bugs.launchpad.net/bzr/+bug/192859>:
110
# we had some past problems with the workingtree remembering for too
111
# long what kind of object was at a particular name; we really
112
# shouldn't do that. Operating on the dirstate through passing
113
# inventory deltas rather than mutating the inventory largely avoids
115
tree = self.make_branch_and_tree('tree')
116
self.build_tree_contents([
118
('tree/a/file', 'content'),
120
tree.smart_add(['tree/a'])
121
tree.commit('add dir')
122
osutils.rmtree('tree/a')
123
self.build_tree_contents([
124
('tree/a@', 'target'),
126
tree.commit('change to symlink')
129
class TestOpenTree(TestCaseWithWorkingTree):
131
_test_needs_features = [tests.SymlinkFeature]
133
def test_open_containing_through_symlink(self):
134
self.make_test_tree()
135
self.check_open_containing('link/content', 'tree', 'content')
136
self.check_open_containing('link/sublink', 'tree', 'sublink')
137
# this next one is a bit debatable, but arguably it's better that
138
# open_containing is only concerned with opening the tree
139
# and then you can deal with symlinks along the way if you want
140
self.check_open_containing('link/sublink/subcontent', 'tree',
141
'sublink/subcontent')
143
def check_open_containing(self, to_open, expected_tree_name,
145
wt, relpath = workingtree.WorkingTree.open_containing(to_open)
146
self.assertEquals(relpath, expected_relpath)
147
self.assertEndsWith(wt.basedir, expected_tree_name)
149
def test_tree_files(self):
150
# not strictly a WorkingTree method, but it should be
151
# probably the root cause for
152
# <https://bugs.launchpad.net/bzr/+bug/128562>
153
self.make_test_tree()
154
self.check_tree_files(['tree/outerlink'],
155
'tree', ['outerlink'])
156
self.check_tree_files(['link/outerlink'],
157
'tree', ['outerlink'])
158
self.check_tree_files(['link/sublink/subcontent'],
159
'tree', ['subdir/subcontent'])
161
def check_tree_files(self, to_open, expected_tree, expect_paths):
162
tree, relpaths = workingtree.WorkingTree.open_containing_paths(to_open)
163
self.assertEndsWith(tree.basedir, expected_tree)
164
self.assertEquals(expect_paths, relpaths)
166
def make_test_tree(self):
167
tree = self.make_branch_and_tree('tree')
168
self.build_tree_contents([
170
('tree/outerlink@', '/not/there'),
171
('tree/content', 'hello'),
172
('tree/sublink@', 'subdir'),
174
('tree/subdir/subcontent', 'subcontent stuff')