1
# Copyright (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
21
from bzrlib import ignores
23
from bzrlib.branch import Branch
24
from bzrlib import bzrdir, conflicts, errors, workingtree
25
from bzrlib.bzrdir import BzrDir
26
from bzrlib.errors import NotBranchError, NotVersionedError
27
from bzrlib.lockdir import LockDir
28
from bzrlib.mutabletree import needs_tree_write_lock
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.symbol_versioning import zero_thirteen
31
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
32
from bzrlib.trace import mutter
33
from bzrlib.transport import get_transport
34
from bzrlib.workingtree import (
42
class TestTreeDirectory(TestCaseWithTransport):
44
def test_kind_character(self):
45
self.assertEqual(TreeDirectory().kind_character(), '/')
48
class TestTreeEntry(TestCaseWithTransport):
50
def test_kind_character(self):
51
self.assertEqual(TreeEntry().kind_character(), '???')
54
class TestTreeFile(TestCaseWithTransport):
56
def test_kind_character(self):
57
self.assertEqual(TreeFile().kind_character(), '')
60
class TestTreeLink(TestCaseWithTransport):
62
def test_kind_character(self):
63
self.assertEqual(TreeLink().kind_character(), '')
66
class TestDefaultFormat(TestCaseWithTransport):
68
def test_get_set_default_format(self):
69
old_format = workingtree.WorkingTreeFormat.get_default_format()
71
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
72
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
74
# the default branch format is used by the meta dir format
75
# which is not the default bzrdir format at this point
76
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
77
dir.create_repository()
79
result = dir.create_workingtree()
80
self.assertEqual(result, 'A tree')
82
workingtree.WorkingTreeFormat.set_default_format(old_format)
83
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
86
class SampleTreeFormat(workingtree.WorkingTreeFormat):
89
this format is initializable, unsupported to aid in testing the
90
open and open_downlevel routines.
93
def get_format_string(self):
94
"""See WorkingTreeFormat.get_format_string()."""
95
return "Sample tree format."
97
def initialize(self, a_bzrdir, revision_id=None):
98
"""Sample branches cannot be created."""
99
t = a_bzrdir.get_workingtree_transport(self)
100
t.put_bytes('format', self.get_format_string())
103
def is_supported(self):
106
def open(self, transport, _found=False):
107
return "opened tree."
110
class TestWorkingTreeFormat(TestCaseWithTransport):
111
"""Tests for the WorkingTreeFormat facility."""
113
def test_find_format(self):
114
# is the right format object found for a working tree?
115
# create a branch with a few known format objects.
116
self.build_tree(["foo/", "bar/"])
117
def check_format(format, url):
118
dir = format._matchingbzrdir.initialize(url)
119
dir.create_repository()
121
format.initialize(dir)
122
t = get_transport(url)
123
found_format = workingtree.WorkingTreeFormat.find_format(dir)
124
self.failUnless(isinstance(found_format, format.__class__))
125
check_format(workingtree.WorkingTreeFormat3(), "bar")
127
def test_find_format_no_tree(self):
128
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
129
self.assertRaises(errors.NoWorkingTree,
130
workingtree.WorkingTreeFormat.find_format,
133
def test_find_format_unknown_format(self):
134
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
135
dir.create_repository()
137
SampleTreeFormat().initialize(dir)
138
self.assertRaises(errors.UnknownFormatError,
139
workingtree.WorkingTreeFormat.find_format,
142
def test_register_unregister_format(self):
143
format = SampleTreeFormat()
145
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
146
dir.create_repository()
149
format.initialize(dir)
150
# register a format for it.
151
workingtree.WorkingTreeFormat.register_format(format)
152
# which branch.Open will refuse (not supported)
153
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
154
# but open_downlevel will work
155
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
156
# unregister the format
157
workingtree.WorkingTreeFormat.unregister_format(format)
160
class TestWorkingTreeFormat3(TestCaseWithTransport):
161
"""Tests specific to WorkingTreeFormat3."""
163
def test_disk_layout(self):
164
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
165
control.create_repository()
166
control.create_branch()
167
tree = workingtree.WorkingTreeFormat3().initialize(control)
169
# format 'Bazaar-NG Working Tree format 3'
170
# inventory = blank inventory
171
# pending-merges = ''
173
# no inventory.basis yet
174
t = control.get_workingtree_transport(None)
175
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
176
t.get('format').read())
177
# self.assertContainsRe(t.get('inventory').read(),
178
# '<inventory file_id="[^"]*" format="5">\n'
181
# WorkingTreeFormat3 doesn't default to creating a unique root id,
182
# because it is incompatible with older bzr versions
183
self.assertContainsRe(t.get('inventory').read(),
184
'<inventory format="5">\n'
187
self.assertEqualDiff('### bzr hashcache v5\n',
188
t.get('stat-cache').read())
189
self.assertFalse(t.has('inventory.basis'))
190
# no last-revision file means 'None' or 'NULLREVISION'
191
self.assertFalse(t.has('last-revision'))
192
# TODO RBC 20060210 do a commit, check the inventory.basis is created
193
# correctly and last-revision file becomes present.
195
def test_uses_lockdir(self):
196
"""WorkingTreeFormat3 uses its own LockDir:
198
- lock is a directory
199
- when the WorkingTree is locked, LockDir can see that
201
t = self.get_transport()
203
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
204
repo = dir.create_repository()
205
branch = dir.create_branch()
207
tree = workingtree.WorkingTreeFormat3().initialize(dir)
208
except errors.NotLocalUrl:
209
raise TestSkipped('Not a local URL')
210
self.assertIsDirectory('.bzr', t)
211
self.assertIsDirectory('.bzr/checkout', t)
212
self.assertIsDirectory('.bzr/checkout/lock', t)
213
our_lock = LockDir(t, '.bzr/checkout/lock')
214
self.assertEquals(our_lock.peek(), None)
216
self.assertTrue(our_lock.peek())
218
self.assertEquals(our_lock.peek(), None)
220
def test_missing_pending_merges(self):
221
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
222
control.create_repository()
223
control.create_branch()
224
tree = workingtree.WorkingTreeFormat3().initialize(control)
225
tree._control_files._transport.delete("pending-merges")
226
self.assertEqual([], tree.get_parent_ids())
229
class TestFormat2WorkingTree(TestCaseWithTransport):
230
"""Tests that are specific to format 2 trees."""
232
def create_format2_tree(self, url):
233
return self.make_branch_and_tree(
234
url, format=bzrlib.bzrdir.BzrDirFormat6())
236
def test_conflicts(self):
237
# test backwards compatability
238
tree = self.create_format2_tree('.')
239
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
241
file('lala.BASE', 'wb').write('labase')
242
expected = conflicts.ContentsConflict('lala')
243
self.assertEqual(list(tree.conflicts()), [expected])
244
file('lala', 'wb').write('la')
245
tree.add('lala', 'lala-id')
246
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
247
self.assertEqual(list(tree.conflicts()), [expected])
248
file('lala.THIS', 'wb').write('lathis')
249
file('lala.OTHER', 'wb').write('laother')
250
# When "text conflict"s happen, stem, THIS and OTHER are text
251
expected = conflicts.TextConflict('lala', file_id='lala-id')
252
self.assertEqual(list(tree.conflicts()), [expected])
253
os.unlink('lala.OTHER')
254
os.mkdir('lala.OTHER')
255
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
256
self.assertEqual(list(tree.conflicts()), [expected])
259
class TestNonFormatSpecificCode(TestCaseWithTransport):
260
"""This class contains tests of workingtree that are not format specific."""
262
def test_gen_file_id(self):
263
file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_file_id,
265
self.assertStartsWith(file_id, 'filename-')
267
def test_gen_root_id(self):
268
file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_root_id)
269
self.assertStartsWith(file_id, 'tree_root-')
272
class InstrumentedTree(object):
273
"""A instrumented tree to check the needs_tree_write_lock decorator."""
278
def lock_tree_write(self):
279
self._locks.append('t')
281
@needs_tree_write_lock
282
def method_with_tree_write_lock(self, *args, **kwargs):
283
"""A lock_tree_write decorated method that returns its arguments."""
286
@needs_tree_write_lock
287
def method_that_raises(self):
288
"""This method causes an exception when called with parameters.
290
This allows the decorator code to be checked - it should still call
295
self._locks.append('u')
298
class TestInstrumentedTree(TestCase):
300
def test_needs_tree_write_lock(self):
301
"""@needs_tree_write_lock should be semantically transparent."""
302
tree = InstrumentedTree()
304
'method_with_tree_write_lock',
305
tree.method_with_tree_write_lock.__name__)
307
"A lock_tree_write decorated method that returns its arguments.",
308
tree.method_with_tree_write_lock.__doc__)
311
result = tree.method_with_tree_write_lock(1,2,3, a='b')
312
self.assertEqual((args, kwargs), result)
313
self.assertEqual(['t', 'u'], tree._locks)
314
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
315
self.assertEqual(['t', 'u', 't', 'u'], tree._locks)