18
18
from cStringIO import StringIO
21
from bzrlib import ignores
22
23
from bzrlib.branch import Branch
23
import bzrlib.bzrdir as bzrdir
24
from bzrlib import bzrdir, conflicts, errors, workingtree
24
25
from bzrlib.bzrdir import BzrDir
25
from bzrlib.conflicts import *
26
import bzrlib.errors as errors
27
26
from bzrlib.errors import NotBranchError, NotVersionedError
28
27
from bzrlib.lockdir import LockDir
28
from bzrlib.mutabletree import needs_tree_write_lock
29
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.tests import TestCaseWithTransport, TestSkipped
30
from bzrlib.symbol_versioning import zero_thirteen
31
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
31
32
from bzrlib.trace import mutter
32
33
from bzrlib.transport import get_transport
33
import bzrlib.workingtree as workingtree
34
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
34
from bzrlib.workingtree import (
37
42
class TestTreeDirectory(TestCaseWithTransport):
206
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."""
208
232
def create_format2_tree(self, url):
209
return BzrDir.create_standalone_workingtree(url)
233
return self.make_branch_and_tree(
234
url, format=bzrlib.bzrdir.BzrDirFormat6())
211
def test_conflicts_format2(self):
236
def test_conflicts(self):
212
237
# test backwards compatability
213
238
tree = self.create_format2_tree('.')
214
239
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
216
241
file('lala.BASE', 'wb').write('labase')
217
expected = ContentsConflict('lala')
242
expected = conflicts.ContentsConflict('lala')
218
243
self.assertEqual(list(tree.conflicts()), [expected])
219
244
file('lala', 'wb').write('la')
220
245
tree.add('lala', 'lala-id')
221
expected = ContentsConflict('lala', file_id='lala-id')
246
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
222
247
self.assertEqual(list(tree.conflicts()), [expected])
223
248
file('lala.THIS', 'wb').write('lathis')
224
249
file('lala.OTHER', 'wb').write('laother')
225
250
# When "text conflict"s happen, stem, THIS and OTHER are text
226
expected = TextConflict('lala', file_id='lala-id')
251
expected = conflicts.TextConflict('lala', file_id='lala-id')
227
252
self.assertEqual(list(tree.conflicts()), [expected])
228
253
os.unlink('lala.OTHER')
229
254
os.mkdir('lala.OTHER')
230
expected = ContentsConflict('lala', file_id='lala-id')
255
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
231
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-')
271
def test__translate_ignore_rule(self):
272
tree = self.make_branch_and_tree('.')
273
# translation should return the regex, the number of groups in it,
274
# and the original rule in a tuple.
275
# there are three sorts of ignore rules:
276
# root only - regex is the rule itself without the leading ./
279
tree._translate_ignore_rule("./rootdirrule"))
280
# full path - regex is the rule itself
282
"(path\\/to\\/file$)",
283
tree._translate_ignore_rule("path/to/file"))
284
# basename only rule - regex is a rule that ignores everything up
285
# to the last / in the filename
287
"((?:.*/)?(?!.*/)basenamerule$)",
288
tree._translate_ignore_rule("basenamerule"))
290
def test__combine_ignore_rules(self):
291
tree = self.make_branch_and_tree('.')
292
# the combined ignore regexs need the outer group indices
293
# placed in a dictionary with the rules that were combined.
294
# an empty set of rules
295
# this is returned as a list of combined regex,rule sets, because
296
# python has a limit of 100 combined regexes.
297
compiled_rules = tree._combine_ignore_rules([])
298
self.assertEqual([], compiled_rules)
299
# one of each type of rule.
300
compiled_rules = tree._combine_ignore_rules(
301
["rule1", "rule/two", "./three"])[0]
302
# what type *is* the compiled regex to do an isinstance of ?
303
self.assertEqual(3, compiled_rules[0].groups)
305
{0:"rule1",1:"rule/two",2:"./three"},
308
def test__combine_ignore_rules_grouping(self):
309
tree = self.make_branch_and_tree('.')
310
# when there are too many rules, the output is split into groups of 100
312
for index in range(198):
314
self.assertEqual(2, len(tree._combine_ignore_rules(rules)))
316
def test__get_ignore_rules_as_regex(self):
317
tree = self.make_branch_and_tree('.')
318
# Setup the default ignore list to be empty
319
ignores._set_user_ignores([])
321
# some plugins (shelf) modifies the DEFAULT_IGNORE list in memory
322
# which causes this test to fail so force the DEFAULT_IGNORE
324
orig_default = bzrlib.DEFAULT_IGNORE
325
# Also make sure the runtime ignore list is empty
326
orig_runtime = ignores._runtime_ignores
328
bzrlib.DEFAULT_IGNORE = []
329
ignores._runtime_ignores = set()
331
self.build_tree_contents([('.bzrignore', 'CVS\n.hg\n')])
332
reference_output = tree._combine_ignore_rules(
333
set(['CVS', '.hg']))[0]
334
regex_rules = tree._get_ignore_rules_as_regex()[0]
335
self.assertEqual(len(reference_output[1]), regex_rules[0].groups)
336
self.assertEqual(reference_output[1], regex_rules[1])
338
bzrlib.DEFAULT_IGNORE = orig_default
339
ignores._runtime_ignores = orig_runtime
342
class InstrumentedTree(object):
343
"""A instrumented tree to check the needs_tree_write_lock decorator."""
348
def lock_tree_write(self):
349
self._locks.append('t')
351
@needs_tree_write_lock
352
def method_with_tree_write_lock(self, *args, **kwargs):
353
"""A lock_tree_write decorated method that returns its arguments."""
356
@needs_tree_write_lock
357
def method_that_raises(self):
358
"""This method causes an exception when called with parameters.
360
This allows the decorator code to be checked - it should still call
365
self._locks.append('u')
368
class TestInstrumentedTree(TestCase):
370
def test_needs_tree_write_lock(self):
371
"""@needs_tree_write_lock should be semantically transparent."""
372
tree = InstrumentedTree()
374
'method_with_tree_write_lock',
375
tree.method_with_tree_write_lock.__name__)
377
"A lock_tree_write decorated method that returns its arguments.",
378
tree.method_with_tree_write_lock.__doc__)
381
result = tree.method_with_tree_write_lock(1,2,3, a='b')
382
self.assertEqual((args, kwargs), result)
383
self.assertEqual(['t', 'u'], tree._locks)
384
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
385
self.assertEqual(['t', 'u', 't', 'u'], tree._locks)