1
# (C) 2005,2006 Canonical Ltd
2
# Authors: Robert Collins <robert.collins@canonical.com>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
22
from bzrlib.branch import Branch
23
from bzrlib.bzrdir import BzrDir
24
import bzrlib.errors as errors
25
from bzrlib.errors import NotBranchError, NotVersionedError
26
from bzrlib.tests import TestCaseWithTransport
27
from bzrlib.trace import mutter
28
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
29
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
32
class TestTreeDirectory(TestCaseWithTransport):
34
def test_kind_character(self):
35
self.assertEqual(TreeDirectory().kind_character(), '/')
38
class TestTreeEntry(TestCaseWithTransport):
40
def test_kind_character(self):
41
self.assertEqual(TreeEntry().kind_character(), '???')
44
class TestTreeFile(TestCaseWithTransport):
46
def test_kind_character(self):
47
self.assertEqual(TreeFile().kind_character(), '')
50
class TestTreeLink(TestCaseWithTransport):
52
def test_kind_character(self):
53
self.assertEqual(TreeLink().kind_character(), '')
56
class TestWorkingTree(TestCaseWithTransport):
58
def test_listfiles(self):
59
tree = WorkingTree.create_standalone('.')
61
print >> open('file', 'w'), "content"
63
os.symlink('target', 'symlink')
64
files = list(tree.list_files())
65
self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
66
self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
68
self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
70
def test_open_containing(self):
71
branch = WorkingTree.create_standalone('.').branch
72
wt, relpath = WorkingTree.open_containing()
73
self.assertEqual('', relpath)
74
self.assertEqual(wt.basedir + '/', branch.base)
75
wt, relpath = WorkingTree.open_containing(u'.')
76
self.assertEqual('', relpath)
77
self.assertEqual(wt.basedir + '/', branch.base)
78
wt, relpath = WorkingTree.open_containing('./foo')
79
self.assertEqual('foo', relpath)
80
self.assertEqual(wt.basedir + '/', branch.base)
81
# paths that are urls are just plain wrong for working trees.
82
self.assertRaises(NotBranchError,
83
WorkingTree.open_containing,
84
'file:///' + getcwd())
86
def test_construct_with_branch(self):
87
branch = WorkingTree.create_standalone('.').branch
88
tree = WorkingTree(branch.base, branch)
89
self.assertEqual(branch, tree.branch)
90
self.assertEqual(branch.base, tree.basedir + '/')
92
def test_construct_without_branch(self):
93
branch = WorkingTree.create_standalone('.').branch
94
tree = WorkingTree(branch.base)
95
self.assertEqual(branch.base, tree.branch.base)
96
self.assertEqual(branch.base, tree.basedir + '/')
98
def test_basic_relpath(self):
99
# for comprehensive relpath tests, see whitebox.py.
100
tree = WorkingTree.create_standalone('.')
101
self.assertEqual('child',
102
tree.relpath(pathjoin(getcwd(), 'child')))
104
def test_lock_locks_branch(self):
105
tree = WorkingTree.create_standalone('.')
107
self.assertEqual('r', tree.branch.peek_lock_mode())
109
self.assertEqual(None, tree.branch.peek_lock_mode())
111
self.assertEqual('w', tree.branch.peek_lock_mode())
113
self.assertEqual(None, tree.branch.peek_lock_mode())
115
def get_pullable_trees(self):
116
self.build_tree(['from/', 'from/file', 'to/'])
117
tree = WorkingTree.create_standalone('from')
119
tree.commit('foo', rev_id='A')
120
tree_b = WorkingTree.create_standalone('to')
124
tree_a, tree_b = self.get_pullable_trees()
125
tree_b.pull(tree_a.branch)
126
self.failUnless(tree_b.branch.repository.has_revision('A'))
127
self.assertEqual(['A'], tree_b.branch.revision_history())
129
def test_pull_overwrites(self):
130
tree_a, tree_b = self.get_pullable_trees()
131
tree_b.commit('foo', rev_id='B')
132
self.assertEqual(['B'], tree_b.branch.revision_history())
133
tree_b.pull(tree_a.branch, overwrite=True)
134
self.failUnless(tree_b.branch.repository.has_revision('A'))
135
self.failUnless(tree_b.branch.repository.has_revision('B'))
136
self.assertEqual(['A'], tree_b.branch.revision_history())
138
def test_revert(self):
139
"""Test selected-file revert"""
140
tree = WorkingTree.create_standalone('.')
142
self.build_tree(['hello.txt'])
143
file('hello.txt', 'w').write('initial hello')
145
self.assertRaises(NotVersionedError,
146
tree.revert, ['hello.txt'])
147
tree.add(['hello.txt'])
148
tree.commit('create initial hello.txt')
150
self.check_file_contents('hello.txt', 'initial hello')
151
file('hello.txt', 'w').write('new hello')
152
self.check_file_contents('hello.txt', 'new hello')
154
# revert file modified since last revision
155
tree.revert(['hello.txt'])
156
self.check_file_contents('hello.txt', 'initial hello')
157
self.check_file_contents('hello.txt~', 'new hello')
159
# reverting again does not clobber the backup
160
tree.revert(['hello.txt'])
161
self.check_file_contents('hello.txt', 'initial hello')
162
self.check_file_contents('hello.txt~', 'new hello')
164
def test_unknowns(self):
165
tree = WorkingTree.create_standalone('.')
166
self.build_tree(['hello.txt',
168
self.assertEquals(list(tree.unknowns()),
171
def test_hashcache(self):
172
from bzrlib.tests.test_hashcache import pause
173
tree = WorkingTree.create_standalone('.')
174
self.build_tree(['hello.txt',
176
tree.add('hello.txt')
178
sha = tree.get_file_sha1(tree.path2id('hello.txt'))
179
self.assertEqual(1, tree._hashcache.miss_count)
180
tree2 = WorkingTree('.', tree.branch)
181
sha2 = tree2.get_file_sha1(tree2.path2id('hello.txt'))
182
self.assertEqual(0, tree2._hashcache.miss_count)
183
self.assertEqual(1, tree2._hashcache.hit_count)
185
def test_checkout(self):
186
# at this point as we dont have checkout versions, checkout simply
187
# populates the required files for a working tree at the dir.
188
b = BzrDir.create_branch_and_repo('branch')
189
t = WorkingTree.create(b, 'tree')
190
# as we are moving the ownership to working tree, we will check here
191
# that its split out correctly
192
self.failIfExists('branch/.bzr/inventory')
193
self.failIfExists('branch/.bzr/pending-merges')
195
bzrlib.xml5.serializer_v5.write_inventory(bzrlib.inventory.Inventory(),
197
self.assertFileEqual(sio.getvalue(), 'tree/.bzr/inventory')
198
self.assertFileEqual('', 'tree/.bzr/pending-merges')
200
def test_initialize(self):
201
# initialize should create a working tree and branch in an existing dir
202
t = WorkingTree.create_standalone('.')
204
self.assertEqual(t.branch.base, b.base)
205
t2 = WorkingTree('.')
206
self.assertEqual(t.basedir, t2.basedir)
207
self.assertEqual(b.base, t2.branch.base)
208
# TODO maybe we should check the branch format? not sure if its
211
def test_rename_dirs(self):
212
"""Test renaming directories and the files within them."""
213
wt = self.make_branch_and_tree('.')
215
self.build_tree(['dir/', 'dir/sub/', 'dir/sub/file'])
216
wt.add(['dir', 'dir/sub', 'dir/sub/file'])
218
wt.commit('create initial state')
220
revid = b.revision_history()[0]
221
self.log('first revision_id is {%s}' % revid)
223
inv = b.repository.get_revision_inventory(revid)
224
self.log('contents of inventory: %r' % inv.entries())
226
self.check_inventory_shape(inv,
227
['dir', 'dir/sub', 'dir/sub/file'])
229
wt.rename_one('dir', 'newdir')
231
self.check_inventory_shape(wt.read_working_inventory(),
232
['newdir', 'newdir/sub', 'newdir/sub/file'])
234
wt.rename_one('newdir/sub', 'newdir/newsub')
235
self.check_inventory_shape(wt.read_working_inventory(),
236
['newdir', 'newdir/newsub',
237
'newdir/newsub/file'])
239
def test_add_in_unversioned(self):
240
"""Try to add a file in an unversioned directory.
242
"bzr add" adds the parent as necessary, but simple working tree add
245
from bzrlib.errors import NotVersionedError
246
wt = self.make_branch_and_tree('.')
247
self.build_tree(['foo/',
249
self.assertRaises(NotVersionedError,
253
def test_add_missing(self):
254
# adding a msising file -> NoSuchFile
255
wt = self.make_branch_and_tree('.')
256
self.assertRaises(errors.NoSuchFile, wt.add, 'fpp')
258
def test_remove_verbose(self):
259
#FIXME the remove api should not print or otherwise depend on the
260
# text UI - RBC 20060124
261
wt = self.make_branch_and_tree('.')
262
self.build_tree(['hello'])
264
wt.commit(message='add hello')
267
self.assertEqual(None, self.apply_redirected(None, stdout, stderr,
271
self.assertEqual('? hello\n', stdout.getvalue())
272
self.assertEqual('', stderr.getvalue())
274
def test_clone_trivial(self):
275
wt = self.make_branch_and_tree('source')
276
cloned = wt.clone('target')
277
self.assertEqual(cloned.last_revision(), wt.last_revision())
279
def test_last_revision(self):
280
wt = self.make_branch_and_tree('source')
281
self.assertEqual(None, wt.last_revision())
282
wt.commit('A', allow_pointless=True, rev_id='A')
283
self.assertEqual('A', wt.last_revision())
285
def test_set_last_revision(self):
286
wt = self.make_branch_and_tree('source')
287
self.assertEqual(None, wt.last_revision())
288
# cannot set the last revision to one not in the branch
289
self.assertRaises(errors.NoSuchRevision, wt.set_last_revision, 'A')
290
wt.commit('A', allow_pointless=True, rev_id='A')
291
self.assertEqual('A', wt.last_revision())
292
# None is aways in the branch
293
wt.set_last_revision(None)
294
self.assertEqual(None, wt.last_revision())
295
# and now we can set it to 'A'
296
# because the current format mutates the branch to set it,
297
# we need to alter the branch to let this pass.
298
wt.branch.set_revision_history(['A', 'B'])
299
wt.set_last_revision('A')
300
self.assertEqual('A', wt.last_revision())
302
def test_clone_and_commit_preserves_last_revision(self):
303
wt = self.make_branch_and_tree('source')
304
cloned = wt.clone('target')
305
wt.commit('A', allow_pointless=True, rev_id='A')
306
self.assertNotEqual(cloned.last_revision(), wt.last_revision())
308
def test_basis_tree_returns_last_revision(self):
309
wt = self.make_branch_and_tree('.')
310
self.build_tree(['foo'])
311
wt.add('foo', 'foo-id')
312
wt.commit('A', rev_id='A')
313
wt.rename_one('foo', 'bar')
314
wt.commit('B', rev_id='B')
315
wt.set_last_revision('B')
316
tree = wt.basis_tree()
317
self.failUnless(tree.has_filename('bar'))
318
wt.set_last_revision('A')
319
tree = wt.basis_tree()
320
self.failUnless(tree.has_filename('foo'))