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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
from cStringIO import StringIO
21
from bzrlib import ignores
28
23
from bzrlib.branch import Branch
24
from bzrlib import bzrdir, conflicts, errors, workingtree
29
25
from bzrlib.bzrdir import BzrDir
26
from bzrlib.errors import NotBranchError, NotVersionedError
30
27
from bzrlib.lockdir import LockDir
31
28
from bzrlib.mutabletree import needs_tree_write_lock
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
32
30
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
31
from bzrlib.trace import mutter
32
from bzrlib.transport import get_transport
33
33
from bzrlib.workingtree import (
41
41
class TestTreeDirectory(TestCaseWithTransport):
43
43
def test_kind_character(self):
81
81
workingtree.WorkingTreeFormat.set_default_format(old_format)
82
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
85
class SampleTreeFormat(workingtree.WorkingTreeFormat):
105
86
"""A sample format
107
this format is initializable, unsupported to aid in testing the
88
this format is initializable, unsupported to aid in testing the
108
89
open and open_downlevel routines.
193
173
t = control.get_workingtree_transport(None)
194
174
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
195
175
t.get('format').read())
196
self.assertEqualDiff(t.get('inventory').read(),
197
'<inventory format="5">\n'
176
self.assertEqualDiff('<inventory format="5">\n'
178
t.get('inventory').read())
200
179
self.assertEqualDiff('### bzr hashcache v5\n',
201
180
t.get('stat-cache').read())
202
181
self.assertFalse(t.has('inventory.basis'))
203
182
# no last-revision file means 'None' or 'NULLREVISION'
204
183
self.assertFalse(t.has('last-revision'))
205
# TODO RBC 20060210 do a commit, check the inventory.basis is created
184
# TODO RBC 20060210 do a commit, check the inventory.basis is created
206
185
# correctly and last-revision file becomes present.
208
187
def test_uses_lockdir(self):
209
188
"""WorkingTreeFormat3 uses its own LockDir:
211
190
- lock is a directory
212
191
- when the WorkingTree is locked, LockDir can see that
269
248
self.assertEqual(list(tree.conflicts()), [expected])
251
class TestNonFormatSpecificCode(TestCaseWithTransport):
252
"""This class contains tests of workingtree that are not format specific."""
255
def test_gen_file_id(self):
256
gen_file_id = bzrlib.workingtree.gen_file_id
258
# We try to use the filename if possible
259
self.assertStartsWith(gen_file_id('bar'), 'bar-')
261
# but we squash capitalization, and remove non word characters
262
self.assertStartsWith(gen_file_id('Mwoo oof\t m'), 'mwoooofm-')
264
# We also remove leading '.' characters to prevent hidden file-ids
265
self.assertStartsWith(gen_file_id('..gam.py'), 'gam.py-')
266
self.assertStartsWith(gen_file_id('..Mwoo oof\t m'), 'mwoooofm-')
268
# we remove unicode characters, and still don't end up with a
270
self.assertStartsWith(gen_file_id(u'\xe5\xb5.txt'), 'txt-')
272
# Our current method of generating unique ids adds 33 characters
273
# plus an serial number (log10(N) characters)
274
# to the end of the filename. We now restrict the filename portion to
275
# be <= 20 characters, so the maximum length should now be approx < 60
277
# Test both case squashing and length restriction
278
fid = gen_file_id('A'*50 + '.txt')
279
self.assertStartsWith(fid, 'a'*20 + '-')
280
self.failUnless(len(fid) < 60)
282
# restricting length happens after the other actions, so
283
# we preserve as much as possible
284
fid = gen_file_id('\xe5\xb5..aBcd\tefGhijKLMnop\tqrstuvwxyz')
285
self.assertStartsWith(fid, 'abcdefghijklmnopqrst-')
286
self.failUnless(len(fid) < 60)
288
def test_next_id_suffix(self):
289
bzrlib.workingtree._gen_id_suffix = None
290
bzrlib.workingtree._next_id_suffix()
291
self.assertNotEqual(None, bzrlib.workingtree._gen_id_suffix)
292
bzrlib.workingtree._gen_id_suffix = "foo-"
293
bzrlib.workingtree._gen_id_serial = 1
294
self.assertEqual("foo-2", bzrlib.workingtree._next_id_suffix())
295
self.assertEqual("foo-3", bzrlib.workingtree._next_id_suffix())
296
self.assertEqual("foo-4", bzrlib.workingtree._next_id_suffix())
297
self.assertEqual("foo-5", bzrlib.workingtree._next_id_suffix())
298
self.assertEqual("foo-6", bzrlib.workingtree._next_id_suffix())
299
self.assertEqual("foo-7", bzrlib.workingtree._next_id_suffix())
300
self.assertEqual("foo-8", bzrlib.workingtree._next_id_suffix())
301
self.assertEqual("foo-9", bzrlib.workingtree._next_id_suffix())
302
self.assertEqual("foo-10", bzrlib.workingtree._next_id_suffix())
304
def test__translate_ignore_rule(self):
305
tree = self.make_branch_and_tree('.')
306
# translation should return the regex, the number of groups in it,
307
# and the original rule in a tuple.
308
# there are three sorts of ignore rules:
309
# root only - regex is the rule itself without the leading ./
312
tree._translate_ignore_rule("./rootdirrule"))
313
# full path - regex is the rule itself
315
"(path\\/to\\/file$)",
316
tree._translate_ignore_rule("path/to/file"))
317
# basename only rule - regex is a rule that ignores everything up
318
# to the last / in the filename
320
"((?:.*/)?(?!.*/)basenamerule$)",
321
tree._translate_ignore_rule("basenamerule"))
323
def test__combine_ignore_rules(self):
324
tree = self.make_branch_and_tree('.')
325
# the combined ignore regexs need the outer group indices
326
# placed in a dictionary with the rules that were combined.
327
# an empty set of rules
328
# this is returned as a list of combined regex,rule sets, because
329
# python has a limit of 100 combined regexes.
330
compiled_rules = tree._combine_ignore_rules([])
331
self.assertEqual([], compiled_rules)
332
# one of each type of rule.
333
compiled_rules = tree._combine_ignore_rules(
334
["rule1", "rule/two", "./three"])[0]
335
# what type *is* the compiled regex to do an isinstance of ?
336
self.assertEqual(3, compiled_rules[0].groups)
338
{0:"rule1",1:"rule/two",2:"./three"},
341
def test__combine_ignore_rules_grouping(self):
342
tree = self.make_branch_and_tree('.')
343
# when there are too many rules, the output is split into groups of 100
345
for index in range(198):
347
self.assertEqual(2, len(tree._combine_ignore_rules(rules)))
349
def test__get_ignore_rules_as_regex(self):
350
tree = self.make_branch_and_tree('.')
351
# Setup the default ignore list to be empty
352
ignores._set_user_ignores([])
354
# some plugins (shelf) modifies the DEFAULT_IGNORE list in memory
355
# which causes this test to fail so force the DEFAULT_IGNORE
357
orig_default = bzrlib.DEFAULT_IGNORE
358
# Also make sure the runtime ignore list is empty
359
orig_runtime = ignores._runtime_ignores
361
bzrlib.DEFAULT_IGNORE = []
362
ignores._runtime_ignores = set()
364
self.build_tree_contents([('.bzrignore', 'CVS\n.hg\n')])
365
reference_output = tree._combine_ignore_rules(
366
set(['CVS', '.hg']))[0]
367
regex_rules = tree._get_ignore_rules_as_regex()[0]
368
self.assertEqual(len(reference_output[1]), regex_rules[0].groups)
369
self.assertEqual(reference_output[1], regex_rules[1])
371
bzrlib.DEFAULT_IGNORE = orig_default
372
ignores._runtime_ignores = orig_runtime
272
375
class InstrumentedTree(object):
273
376
"""A instrumented tree to check the needs_tree_write_lock decorator."""
313
416
self.assertEqual(['t', 'u'], tree._locks)
314
417
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
315
418
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)))