219
233
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
221
235
file('lala.BASE', 'wb').write('labase')
222
expected = ContentsConflict('lala')
236
expected = conflicts.ContentsConflict('lala')
223
237
self.assertEqual(list(tree.conflicts()), [expected])
224
238
file('lala', 'wb').write('la')
225
239
tree.add('lala', 'lala-id')
226
expected = ContentsConflict('lala', file_id='lala-id')
240
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
227
241
self.assertEqual(list(tree.conflicts()), [expected])
228
242
file('lala.THIS', 'wb').write('lathis')
229
243
file('lala.OTHER', 'wb').write('laother')
230
244
# When "text conflict"s happen, stem, THIS and OTHER are text
231
expected = TextConflict('lala', file_id='lala-id')
245
expected = conflicts.TextConflict('lala', file_id='lala-id')
232
246
self.assertEqual(list(tree.conflicts()), [expected])
233
247
os.unlink('lala.OTHER')
234
248
os.mkdir('lala.OTHER')
235
expected = ContentsConflict('lala', file_id='lala-id')
249
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
236
250
self.assertEqual(list(tree.conflicts()), [expected])
239
class TestNonFormatSpecificCode(TestCaseWithTransport):
240
"""This class contains tests of workingtree that are not format specific."""
243
def test_gen_file_id(self):
244
self.assertStartsWith(bzrlib.workingtree.gen_file_id('bar'), 'bar-')
245
self.assertStartsWith(bzrlib.workingtree.gen_file_id('Mwoo oof\t m'), 'Mwoooofm-')
246
self.assertStartsWith(bzrlib.workingtree.gen_file_id('..gam.py'), 'gam.py-')
247
self.assertStartsWith(bzrlib.workingtree.gen_file_id('..Mwoo oof\t m'), 'Mwoooofm-')
249
def test_next_id_suffix(self):
250
bzrlib.workingtree._gen_id_suffix = None
251
bzrlib.workingtree._next_id_suffix()
252
self.assertNotEqual(None, bzrlib.workingtree._gen_id_suffix)
253
bzrlib.workingtree._gen_id_suffix = "foo-"
254
bzrlib.workingtree._gen_id_serial = 1
255
self.assertEqual("foo-2", bzrlib.workingtree._next_id_suffix())
256
self.assertEqual("foo-3", bzrlib.workingtree._next_id_suffix())
257
self.assertEqual("foo-4", bzrlib.workingtree._next_id_suffix())
258
self.assertEqual("foo-5", bzrlib.workingtree._next_id_suffix())
259
self.assertEqual("foo-6", bzrlib.workingtree._next_id_suffix())
260
self.assertEqual("foo-7", bzrlib.workingtree._next_id_suffix())
261
self.assertEqual("foo-8", bzrlib.workingtree._next_id_suffix())
262
self.assertEqual("foo-9", bzrlib.workingtree._next_id_suffix())
263
self.assertEqual("foo-10", bzrlib.workingtree._next_id_suffix())
265
def test__translate_ignore_rule(self):
266
tree = self.make_branch_and_tree('.')
267
# translation should return the regex, the number of groups in it,
268
# and the original rule in a tuple.
269
# there are three sorts of ignore rules:
270
# root only - regex is the rule itself without the leading ./
273
tree._translate_ignore_rule("./rootdirrule"))
274
# full path - regex is the rule itself
276
"(path\\/to\\/file$)",
277
tree._translate_ignore_rule("path/to/file"))
278
# basename only rule - regex is a rule that ignores everything up
279
# to the last / in the filename
281
"((?:.*/)?(?!.*/)basenamerule$)",
282
tree._translate_ignore_rule("basenamerule"))
284
def test__combine_ignore_rules(self):
285
tree = self.make_branch_and_tree('.')
286
# the combined ignore regexs need the outer group indices
287
# placed in a dictionary with the rules that were combined.
288
# an empty set of rules
289
# this is returned as a list of combined regex,rule sets, because
290
# python has a limit of 100 combined regexes.
291
compiled_rules = tree._combine_ignore_rules([])
292
self.assertEqual([], compiled_rules)
293
# one of each type of rule.
294
compiled_rules = tree._combine_ignore_rules(
295
["rule1", "rule/two", "./three"])[0]
296
# what type *is* the compiled regex to do an isinstance of ?
297
self.assertEqual(3, compiled_rules[0].groups)
299
{0:"rule1",1:"rule/two",2:"./three"},
302
def test__combine_ignore_rules_grouping(self):
303
tree = self.make_branch_and_tree('.')
304
# when there are too many rules, the output is split into groups of 100
306
for index in range(198):
308
self.assertEqual(2, len(tree._combine_ignore_rules(rules)))
310
def test__get_ignore_rules_as_regex(self):
311
tree = self.make_branch_and_tree('.')
312
# test against the default rules.
313
reference_output = tree._combine_ignore_rules(bzrlib.DEFAULT_IGNORE)[0]
314
regex_rules = tree._get_ignore_rules_as_regex()[0]
315
self.assertEqual(len(reference_output[1]), regex_rules[0].groups)
316
self.assertEqual(reference_output[1], regex_rules[1])
253
class InstrumentedTree(object):
254
"""A instrumented tree to check the needs_tree_write_lock decorator."""
259
def lock_tree_write(self):
260
self._locks.append('t')
262
@needs_tree_write_lock
263
def method_with_tree_write_lock(self, *args, **kwargs):
264
"""A lock_tree_write decorated method that returns its arguments."""
267
@needs_tree_write_lock
268
def method_that_raises(self):
269
"""This method causes an exception when called with parameters.
271
This allows the decorator code to be checked - it should still call
276
self._locks.append('u')
279
class TestInstrumentedTree(TestCase):
281
def test_needs_tree_write_lock(self):
282
"""@needs_tree_write_lock should be semantically transparent."""
283
tree = InstrumentedTree()
285
'method_with_tree_write_lock',
286
tree.method_with_tree_write_lock.__name__)
288
"A lock_tree_write decorated method that returns its arguments.",
289
tree.method_with_tree_write_lock.__doc__)
292
result = tree.method_with_tree_write_lock(1,2,3, a='b')
293
self.assertEqual((args, kwargs), result)
294
self.assertEqual(['t', 'u'], tree._locks)
295
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
296
self.assertEqual(['t', 'u', 't', 'u'], tree._locks)
299
class TestRevert(TestCaseWithTransport):
301
def test_revert_conflicts_recursive(self):
302
this_tree = self.make_branch_and_tree('this-tree')
303
self.build_tree_contents([('this-tree/foo/',),
304
('this-tree/foo/bar', 'bar')])
305
this_tree.add(['foo', 'foo/bar'])
306
this_tree.commit('created foo/bar')
307
other_tree = this_tree.bzrdir.sprout('other-tree').open_workingtree()
308
self.build_tree_contents([('other-tree/foo/bar', 'baz')])
309
other_tree.commit('changed bar')
310
self.build_tree_contents([('this-tree/foo/bar', 'qux')])
311
this_tree.commit('changed qux')
312
this_tree.merge_from_branch(other_tree.branch)
313
self.assertEqual(1, len(this_tree.conflicts()))
314
this_tree.revert(['foo'])
315
self.assertEqual(0, len(this_tree.conflicts()))
318
class TestAutoResolve(TestCaseWithTransport):
320
def test_auto_resolve(self):
321
base = self.make_branch_and_tree('base')
322
self.build_tree_contents([('base/hello', 'Hello')])
323
base.add('hello', 'hello_id')
325
other = base.bzrdir.sprout('other').open_workingtree()
326
self.build_tree_contents([('other/hello', 'hELLO')])
327
other.commit('Case switch')
328
this = base.bzrdir.sprout('this').open_workingtree()
329
self.failUnlessExists('this/hello')
330
self.build_tree_contents([('this/hello', 'Hello World')])
331
this.commit('Add World')
332
this.merge_from_branch(other.branch)
333
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
336
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
338
self.build_tree_contents([('this/hello', '<<<<<<<')])
340
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
342
self.build_tree_contents([('this/hello', '=======')])
344
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
346
self.build_tree_contents([('this/hello', '\n>>>>>>>')])
347
remaining, resolved = this.auto_resolve()
348
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
350
self.assertEqual([], resolved)
351
self.build_tree_contents([('this/hello', 'hELLO wORLD')])
352
remaining, resolved = this.auto_resolve()
353
self.assertEqual([], this.conflicts())
354
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
356
self.failIfExists('this/hello.BASE')
358
def test_auto_resolve_dir(self):
359
tree = self.make_branch_and_tree('tree')
360
self.build_tree(['tree/hello/'])
361
tree.add('hello', 'hello-id')
362
file_conflict = conflicts.TextConflict('file', None, 'hello-id')
363
tree.set_conflicts(conflicts.ConflictList([file_conflict]))
367
class TestFindTrees(TestCaseWithTransport):
369
def test_find_trees(self):
370
self.make_branch_and_tree('foo')
371
self.make_branch_and_tree('foo/bar')
372
# Sticking a tree inside a control dir is heinous, so let's skip it
373
self.make_branch_and_tree('foo/.bzr/baz')
374
self.make_branch('qux')
375
trees = workingtree.WorkingTree.find_trees('.')
376
self.assertEqual(2, len(list(trees)))