14
14
# You should have received a copy of the GNU General Public License
15
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
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from cStringIO import StringIO
19
27
from bzrlib.branch import Branch
20
from bzrlib.selftest import TestCaseInTempDir
21
from bzrlib.trace import mutter
22
from bzrlib.workingtree import TreeEntry, TreeDirectory, TreeFile, TreeLink
24
class TestTreeDirectory(TestCaseInTempDir):
28
from bzrlib.bzrdir import BzrDir
29
from bzrlib.lockdir import LockDir
30
from bzrlib.mutabletree import needs_tree_write_lock
31
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
32
from bzrlib.transport import get_transport
33
from bzrlib.workingtree import (
41
class TestTreeDirectory(TestCaseWithTransport):
26
43
def test_kind_character(self):
27
44
self.assertEqual(TreeDirectory().kind_character(), '/')
30
class TestTreeEntry(TestCaseInTempDir):
47
class TestTreeEntry(TestCaseWithTransport):
32
49
def test_kind_character(self):
33
50
self.assertEqual(TreeEntry().kind_character(), '???')
36
class TestTreeFile(TestCaseInTempDir):
53
class TestTreeFile(TestCaseWithTransport):
38
55
def test_kind_character(self):
39
56
self.assertEqual(TreeFile().kind_character(), '')
42
class TestTreeLink(TestCaseInTempDir):
59
class TestTreeLink(TestCaseWithTransport):
44
61
def test_kind_character(self):
45
62
self.assertEqual(TreeLink().kind_character(), '')
48
class TestWorkingTree(TestCaseInTempDir):
50
def test_listfiles(self):
51
branch = Branch.initialize('.')
53
print >> open('file', 'w'), "content"
54
os.symlink('target', 'symlink')
55
tree = branch.working_tree()
56
files = list(tree.list_files())
57
self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
58
self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
59
self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
65
class TestDefaultFormat(TestCaseWithTransport):
67
def test_get_set_default_format(self):
68
old_format = workingtree.WorkingTreeFormat.get_default_format()
70
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
71
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
73
# the default branch format is used by the meta dir format
74
# which is not the default bzrdir format at this point
75
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
76
dir.create_repository()
78
result = dir.create_workingtree()
79
self.assertEqual(result, 'A tree')
81
workingtree.WorkingTreeFormat.set_default_format(old_format)
82
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
85
tree = self.make_branch_and_tree('.')
86
open_direct = workingtree.WorkingTree.open('.')
87
self.assertEqual(tree.basedir, open_direct.basedir)
88
open_no_args = workingtree.WorkingTree.open()
89
self.assertEqual(tree.basedir, open_no_args.basedir)
91
def test_open_containing(self):
92
tree = self.make_branch_and_tree('.')
93
open_direct, relpath = workingtree.WorkingTree.open_containing('.')
94
self.assertEqual(tree.basedir, open_direct.basedir)
95
self.assertEqual('', relpath)
96
open_no_args, relpath = workingtree.WorkingTree.open_containing()
97
self.assertEqual(tree.basedir, open_no_args.basedir)
98
self.assertEqual('', relpath)
99
open_subdir, relpath = workingtree.WorkingTree.open_containing('subdir')
100
self.assertEqual(tree.basedir, open_subdir.basedir)
101
self.assertEqual('subdir', relpath)
104
class SampleTreeFormat(workingtree.WorkingTreeFormat):
107
this format is initializable, unsupported to aid in testing the
108
open and open_downlevel routines.
111
def get_format_string(self):
112
"""See WorkingTreeFormat.get_format_string()."""
113
return "Sample tree format."
115
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
116
accelerator_tree=None, hardlink=False):
117
"""Sample branches cannot be created."""
118
t = a_bzrdir.get_workingtree_transport(self)
119
t.put_bytes('format', self.get_format_string())
122
def is_supported(self):
125
def open(self, transport, _found=False):
126
return "opened tree."
129
class TestWorkingTreeFormat(TestCaseWithTransport):
130
"""Tests for the WorkingTreeFormat facility."""
132
def test_find_format(self):
133
# is the right format object found for a working tree?
134
# create a branch with a few known format objects.
135
self.build_tree(["foo/", "bar/"])
136
def check_format(format, url):
137
dir = format._matchingbzrdir.initialize(url)
138
dir.create_repository()
140
format.initialize(dir)
141
t = get_transport(url)
142
found_format = workingtree.WorkingTreeFormat.find_format(dir)
143
self.failUnless(isinstance(found_format, format.__class__))
144
check_format(workingtree.WorkingTreeFormat3(), "bar")
146
def test_find_format_no_tree(self):
147
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
148
self.assertRaises(errors.NoWorkingTree,
149
workingtree.WorkingTreeFormat.find_format,
152
def test_find_format_unknown_format(self):
153
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
154
dir.create_repository()
156
SampleTreeFormat().initialize(dir)
157
self.assertRaises(errors.UnknownFormatError,
158
workingtree.WorkingTreeFormat.find_format,
161
def test_register_unregister_format(self):
162
format = SampleTreeFormat()
164
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
165
dir.create_repository()
168
format.initialize(dir)
169
# register a format for it.
170
workingtree.WorkingTreeFormat.register_format(format)
171
# which branch.Open will refuse (not supported)
172
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
173
# but open_downlevel will work
174
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
175
# unregister the format
176
workingtree.WorkingTreeFormat.unregister_format(format)
179
class TestWorkingTreeFormat3(TestCaseWithTransport):
180
"""Tests specific to WorkingTreeFormat3."""
182
def test_disk_layout(self):
183
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
184
control.create_repository()
185
control.create_branch()
186
tree = workingtree.WorkingTreeFormat3().initialize(control)
188
# format 'Bazaar-NG Working Tree format 3'
189
# inventory = blank inventory
190
# pending-merges = ''
192
# no inventory.basis yet
193
t = control.get_workingtree_transport(None)
194
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
195
t.get('format').read())
196
self.assertEqualDiff(t.get('inventory').read(),
197
'<inventory format="5">\n'
200
self.assertEqualDiff('### bzr hashcache v5\n',
201
t.get('stat-cache').read())
202
self.assertFalse(t.has('inventory.basis'))
203
# no last-revision file means 'None' or 'NULLREVISION'
204
self.assertFalse(t.has('last-revision'))
205
# TODO RBC 20060210 do a commit, check the inventory.basis is created
206
# correctly and last-revision file becomes present.
208
def test_uses_lockdir(self):
209
"""WorkingTreeFormat3 uses its own LockDir:
211
- lock is a directory
212
- when the WorkingTree is locked, LockDir can see that
214
t = self.get_transport()
216
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
217
repo = dir.create_repository()
218
branch = dir.create_branch()
220
tree = workingtree.WorkingTreeFormat3().initialize(dir)
221
except errors.NotLocalUrl:
222
raise TestSkipped('Not a local URL')
223
self.assertIsDirectory('.bzr', t)
224
self.assertIsDirectory('.bzr/checkout', t)
225
self.assertIsDirectory('.bzr/checkout/lock', t)
226
our_lock = LockDir(t, '.bzr/checkout/lock')
227
self.assertEquals(our_lock.peek(), None)
229
self.assertTrue(our_lock.peek())
231
self.assertEquals(our_lock.peek(), None)
233
def test_missing_pending_merges(self):
234
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
235
control.create_repository()
236
control.create_branch()
237
tree = workingtree.WorkingTreeFormat3().initialize(control)
238
tree._transport.delete("pending-merges")
239
self.assertEqual([], tree.get_parent_ids())
242
class TestFormat2WorkingTree(TestCaseWithTransport):
243
"""Tests that are specific to format 2 trees."""
245
def create_format2_tree(self, url):
246
return self.make_branch_and_tree(
247
url, format=bzrdir.BzrDirFormat6())
249
def test_conflicts(self):
250
# test backwards compatability
251
tree = self.create_format2_tree('.')
252
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
254
file('lala.BASE', 'wb').write('labase')
255
expected = conflicts.ContentsConflict('lala')
256
self.assertEqual(list(tree.conflicts()), [expected])
257
file('lala', 'wb').write('la')
258
tree.add('lala', 'lala-id')
259
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
260
self.assertEqual(list(tree.conflicts()), [expected])
261
file('lala.THIS', 'wb').write('lathis')
262
file('lala.OTHER', 'wb').write('laother')
263
# When "text conflict"s happen, stem, THIS and OTHER are text
264
expected = conflicts.TextConflict('lala', file_id='lala-id')
265
self.assertEqual(list(tree.conflicts()), [expected])
266
os.unlink('lala.OTHER')
267
os.mkdir('lala.OTHER')
268
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
269
self.assertEqual(list(tree.conflicts()), [expected])
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)
318
class TestRevert(TestCaseWithTransport):
320
def test_revert_conflicts_recursive(self):
321
this_tree = self.make_branch_and_tree('this-tree')
322
self.build_tree_contents([('this-tree/foo/',),
323
('this-tree/foo/bar', 'bar')])
324
this_tree.add(['foo', 'foo/bar'])
325
this_tree.commit('created foo/bar')
326
other_tree = this_tree.bzrdir.sprout('other-tree').open_workingtree()
327
self.build_tree_contents([('other-tree/foo/bar', 'baz')])
328
other_tree.commit('changed bar')
329
self.build_tree_contents([('this-tree/foo/bar', 'qux')])
330
this_tree.commit('changed qux')
331
this_tree.merge_from_branch(other_tree.branch)
332
self.assertEqual(1, len(this_tree.conflicts()))
333
this_tree.revert(['foo'])
334
self.assertEqual(0, len(this_tree.conflicts()))
337
class TestAutoResolve(TestCaseWithTransport):
339
def test_auto_resolve(self):
340
base = self.make_branch_and_tree('base')
341
self.build_tree_contents([('base/hello', 'Hello')])
342
base.add('hello', 'hello_id')
344
other = base.bzrdir.sprout('other').open_workingtree()
345
self.build_tree_contents([('other/hello', 'hELLO')])
346
other.commit('Case switch')
347
this = base.bzrdir.sprout('this').open_workingtree()
348
self.failUnlessExists('this/hello')
349
self.build_tree_contents([('this/hello', 'Hello World')])
350
this.commit('Add World')
351
this.merge_from_branch(other.branch)
352
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
355
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
357
self.build_tree_contents([('this/hello', '<<<<<<<')])
359
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
361
self.build_tree_contents([('this/hello', '=======')])
363
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
365
self.build_tree_contents([('this/hello', '\n>>>>>>>')])
366
remaining, resolved = this.auto_resolve()
367
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
369
self.assertEqual([], resolved)
370
self.build_tree_contents([('this/hello', 'hELLO wORLD')])
371
remaining, resolved = this.auto_resolve()
372
self.assertEqual([], this.conflicts())
373
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
375
self.failIfExists('this/hello.BASE')
377
def test_auto_resolve_dir(self):
378
tree = self.make_branch_and_tree('tree')
379
self.build_tree(['tree/hello/'])
380
tree.add('hello', 'hello-id')
381
file_conflict = conflicts.TextConflict('file', None, 'hello-id')
382
tree.set_conflicts(conflicts.ConflictList([file_conflict]))
386
class TestFindTrees(TestCaseWithTransport):
388
def test_find_trees(self):
389
self.make_branch_and_tree('foo')
390
self.make_branch_and_tree('foo/bar')
391
# Sticking a tree inside a control dir is heinous, so let's skip it
392
self.make_branch_and_tree('foo/.bzr/baz')
393
self.make_branch('qux')
394
trees = workingtree.WorkingTree.find_trees('.')
395
self.assertEqual(2, len(list(trees)))