~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_workingtree.py

Merged bzr.dev and updated NEWS with a better description of changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
from cStringIO import StringIO
19
19
import os
20
20
 
21
 
from bzrlib import ignores
22
 
import bzrlib
 
21
from bzrlib import (
 
22
    bzrdir,
 
23
    conflicts,
 
24
    errors,
 
25
    workingtree,
 
26
    )
23
27
from bzrlib.branch import Branch
24
 
from bzrlib import bzrdir, conflicts, errors, workingtree
25
28
from bzrlib.bzrdir import BzrDir
26
 
from bzrlib.errors import NotBranchError, NotVersionedError
27
29
from bzrlib.lockdir import LockDir
28
 
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
29
 
from bzrlib.tests import TestCaseWithTransport, TestSkipped
30
 
from bzrlib.trace import mutter
 
30
from bzrlib.mutabletree import needs_tree_write_lock
 
31
from bzrlib.symbol_versioning import zero_thirteen
 
32
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
31
33
from bzrlib.transport import get_transport
32
 
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
33
 
                                WorkingTree)
 
34
from bzrlib.workingtree import (
 
35
    TreeEntry,
 
36
    TreeDirectory,
 
37
    TreeFile,
 
38
    TreeLink,
 
39
    )
 
40
 
34
41
 
35
42
class TestTreeDirectory(TestCaseWithTransport):
36
43
 
90
97
    def initialize(self, a_bzrdir, revision_id=None):
91
98
        """Sample branches cannot be created."""
92
99
        t = a_bzrdir.get_workingtree_transport(self)
93
 
        t.put('format', StringIO(self.get_format_string()))
 
100
        t.put_bytes('format', self.get_format_string())
94
101
        return 'A tree'
95
102
 
96
103
    def is_supported(self):
167
174
        t = control.get_workingtree_transport(None)
168
175
        self.assertEqualDiff('Bazaar-NG Working Tree format 3',
169
176
                             t.get('format').read())
170
 
        self.assertEqualDiff('<inventory format="5">\n'
171
 
                             '</inventory>\n',
172
 
                             t.get('inventory').read())
 
177
        self.assertEqualDiff(t.get('inventory').read(), 
 
178
                              '<inventory format="5">\n'
 
179
                              '</inventory>\n',
 
180
                             )
173
181
        self.assertEqualDiff('### bzr hashcache v5\n',
174
182
                             t.get('stat-cache').read())
175
183
        self.assertFalse(t.has('inventory.basis'))
209
217
        control.create_branch()
210
218
        tree = workingtree.WorkingTreeFormat3().initialize(control)
211
219
        tree._control_files._transport.delete("pending-merges")
212
 
        self.assertEqual([], tree.pending_merges())
 
220
        self.assertEqual([], tree.get_parent_ids())
213
221
 
214
222
 
215
223
class TestFormat2WorkingTree(TestCaseWithTransport):
217
225
 
218
226
    def create_format2_tree(self, url):
219
227
        return self.make_branch_and_tree(
220
 
            url, format=bzrlib.bzrdir.BzrDirFormat6())
 
228
            url, format=bzrdir.BzrDirFormat6())
221
229
 
222
230
    def test_conflicts(self):
223
231
        # test backwards compatability
245
253
class TestNonFormatSpecificCode(TestCaseWithTransport):
246
254
    """This class contains tests of workingtree that are not format specific."""
247
255
 
248
 
    
249
256
    def test_gen_file_id(self):
250
 
        gen_file_id = bzrlib.workingtree.gen_file_id
251
 
 
252
 
        # We try to use the filename if possible
253
 
        self.assertStartsWith(gen_file_id('bar'), 'bar-')
254
 
 
255
 
        # but we squash capitalization, and remove non word characters
256
 
        self.assertStartsWith(gen_file_id('Mwoo oof\t m'), 'mwoooofm-')
257
 
 
258
 
        # We also remove leading '.' characters to prevent hidden file-ids
259
 
        self.assertStartsWith(gen_file_id('..gam.py'), 'gam.py-')
260
 
        self.assertStartsWith(gen_file_id('..Mwoo oof\t m'), 'mwoooofm-')
261
 
 
262
 
        # we remove unicode characters, and still don't end up with a 
263
 
        # hidden file id
264
 
        self.assertStartsWith(gen_file_id(u'\xe5\xb5.txt'), 'txt-')
265
 
        
266
 
        # Our current method of generating unique ids adds 33 characters
267
 
        # plus an serial number (log10(N) characters)
268
 
        # to the end of the filename. We now restrict the filename portion to
269
 
        # be <= 20 characters, so the maximum length should now be approx < 60
270
 
 
271
 
        # Test both case squashing and length restriction
272
 
        fid = gen_file_id('A'*50 + '.txt')
273
 
        self.assertStartsWith(fid, 'a'*20 + '-')
274
 
        self.failUnless(len(fid) < 60)
275
 
 
276
 
        # restricting length happens after the other actions, so
277
 
        # we preserve as much as possible
278
 
        fid = gen_file_id('\xe5\xb5..aBcd\tefGhijKLMnop\tqrstuvwxyz')
279
 
        self.assertStartsWith(fid, 'abcdefghijklmnopqrst-')
280
 
        self.failUnless(len(fid) < 60)
281
 
 
282
 
    def test_next_id_suffix(self):
283
 
        bzrlib.workingtree._gen_id_suffix = None
284
 
        bzrlib.workingtree._next_id_suffix()
285
 
        self.assertNotEqual(None, bzrlib.workingtree._gen_id_suffix)
286
 
        bzrlib.workingtree._gen_id_suffix = "foo-"
287
 
        bzrlib.workingtree._gen_id_serial = 1
288
 
        self.assertEqual("foo-2", bzrlib.workingtree._next_id_suffix())
289
 
        self.assertEqual("foo-3", bzrlib.workingtree._next_id_suffix())
290
 
        self.assertEqual("foo-4", bzrlib.workingtree._next_id_suffix())
291
 
        self.assertEqual("foo-5", bzrlib.workingtree._next_id_suffix())
292
 
        self.assertEqual("foo-6", bzrlib.workingtree._next_id_suffix())
293
 
        self.assertEqual("foo-7", bzrlib.workingtree._next_id_suffix())
294
 
        self.assertEqual("foo-8", bzrlib.workingtree._next_id_suffix())
295
 
        self.assertEqual("foo-9", bzrlib.workingtree._next_id_suffix())
296
 
        self.assertEqual("foo-10", bzrlib.workingtree._next_id_suffix())
297
 
 
298
 
    def test__translate_ignore_rule(self):
299
 
        tree = self.make_branch_and_tree('.')
300
 
        # translation should return the regex, the number of groups in it,
301
 
        # and the original rule in a tuple.
302
 
        # there are three sorts of ignore rules:
303
 
        # root only - regex is the rule itself without the leading ./
304
 
        self.assertEqual(
305
 
            "(rootdirrule$)", 
306
 
            tree._translate_ignore_rule("./rootdirrule"))
307
 
        # full path - regex is the rule itself
308
 
        self.assertEqual(
309
 
            "(path\\/to\\/file$)",
310
 
            tree._translate_ignore_rule("path/to/file"))
311
 
        # basename only rule - regex is a rule that ignores everything up
312
 
        # to the last / in the filename
313
 
        self.assertEqual(
314
 
            "((?:.*/)?(?!.*/)basenamerule$)",
315
 
            tree._translate_ignore_rule("basenamerule"))
316
 
 
317
 
    def test__combine_ignore_rules(self):
318
 
        tree = self.make_branch_and_tree('.')
319
 
        # the combined ignore regexs need the outer group indices
320
 
        # placed in a dictionary with the rules that were combined.
321
 
        # an empty set of rules
322
 
        # this is returned as a list of combined regex,rule sets, because
323
 
        # python has a limit of 100 combined regexes.
324
 
        compiled_rules = tree._combine_ignore_rules([])
325
 
        self.assertEqual([], compiled_rules)
326
 
        # one of each type of rule.
327
 
        compiled_rules = tree._combine_ignore_rules(
328
 
            ["rule1", "rule/two", "./three"])[0]
329
 
        # what type *is* the compiled regex to do an isinstance of ?
330
 
        self.assertEqual(3, compiled_rules[0].groups)
331
 
        self.assertEqual(
332
 
            {0:"rule1",1:"rule/two",2:"./three"},
333
 
            compiled_rules[1])
334
 
 
335
 
    def test__combine_ignore_rules_grouping(self):
336
 
        tree = self.make_branch_and_tree('.')
337
 
        # when there are too many rules, the output is split into groups of 100
338
 
        rules = []
339
 
        for index in range(198):
340
 
            rules.append('foo')
341
 
        self.assertEqual(2, len(tree._combine_ignore_rules(rules)))
342
 
 
343
 
    def test__get_ignore_rules_as_regex(self):
344
 
        tree = self.make_branch_and_tree('.')
345
 
        # Setup the default ignore list to be empty
346
 
        ignores._set_user_ignores([])
347
 
 
348
 
        # some plugins (shelf) modifies the DEFAULT_IGNORE list in memory
349
 
        # which causes this test to fail so force the DEFAULT_IGNORE
350
 
        # list to be empty
351
 
        orig_default = bzrlib.DEFAULT_IGNORE
352
 
        # Also make sure the runtime ignore list is empty
353
 
        orig_runtime = ignores._runtime_ignores
354
 
        try:
355
 
            bzrlib.DEFAULT_IGNORE = []
356
 
            ignores._runtime_ignores = set()
357
 
 
358
 
            self.build_tree_contents([('.bzrignore', 'CVS\n.hg\n')])
359
 
            reference_output = tree._combine_ignore_rules(
360
 
                                    set(['CVS', '.hg']))[0]
361
 
            regex_rules = tree._get_ignore_rules_as_regex()[0]
362
 
            self.assertEqual(len(reference_output[1]), regex_rules[0].groups)
363
 
            self.assertEqual(reference_output[1], regex_rules[1])
364
 
        finally:
365
 
            bzrlib.DEFAULT_IGNORE = orig_default
366
 
            ignores._runtime_ignores = orig_runtime
 
257
        file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_file_id,
 
258
                                      'filename')
 
259
        self.assertStartsWith(file_id, 'filename-')
 
260
 
 
261
    def test_gen_root_id(self):
 
262
        file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_root_id)
 
263
        self.assertStartsWith(file_id, 'tree_root-')
 
264
        
 
265
 
 
266
class InstrumentedTree(object):
 
267
    """A instrumented tree to check the needs_tree_write_lock decorator."""
 
268
 
 
269
    def __init__(self):
 
270
        self._locks = []
 
271
 
 
272
    def lock_tree_write(self):
 
273
        self._locks.append('t')
 
274
 
 
275
    @needs_tree_write_lock
 
276
    def method_with_tree_write_lock(self, *args, **kwargs):
 
277
        """A lock_tree_write decorated method that returns its arguments."""
 
278
        return args, kwargs
 
279
 
 
280
    @needs_tree_write_lock
 
281
    def method_that_raises(self):
 
282
        """This method causes an exception when called with parameters.
 
283
        
 
284
        This allows the decorator code to be checked - it should still call
 
285
        unlock.
 
286
        """
 
287
 
 
288
    def unlock(self):
 
289
        self._locks.append('u')
 
290
 
 
291
 
 
292
class TestInstrumentedTree(TestCase):
 
293
 
 
294
    def test_needs_tree_write_lock(self):
 
295
        """@needs_tree_write_lock should be semantically transparent."""
 
296
        tree = InstrumentedTree()
 
297
        self.assertEqual(
 
298
            'method_with_tree_write_lock',
 
299
            tree.method_with_tree_write_lock.__name__)
 
300
        self.assertEqual(
 
301
            "A lock_tree_write decorated method that returns its arguments.",
 
302
            tree.method_with_tree_write_lock.__doc__)
 
303
        args = (1, 2, 3)
 
304
        kwargs = {'a':'b'}
 
305
        result = tree.method_with_tree_write_lock(1,2,3, a='b')
 
306
        self.assertEqual((args, kwargs), result)
 
307
        self.assertEqual(['t', 'u'], tree._locks)
 
308
        self.assertRaises(TypeError, tree.method_that_raises, 'foo')
 
309
        self.assertEqual(['t', 'u', 't', 'u'], tree._locks)
 
310
 
 
311
 
 
312
class TestAutoResolve(TestCaseWithTransport):
 
313
 
 
314
    def test_auto_resolve(self):
 
315
        base = self.make_branch_and_tree('base')
 
316
        self.build_tree_contents([('base/hello', 'Hello')])
 
317
        base.add('hello', 'hello_id')
 
318
        base.commit('Hello')
 
319
        other = base.bzrdir.sprout('other').open_workingtree()
 
320
        self.build_tree_contents([('other/hello', 'hELLO')])
 
321
        other.commit('Case switch')
 
322
        this = base.bzrdir.sprout('this').open_workingtree()
 
323
        self.failUnlessExists('this/hello')
 
324
        self.build_tree_contents([('this/hello', 'Hello World')])
 
325
        this.commit('Add World')
 
326
        this.merge_from_branch(other.branch)
 
327
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
 
328
                         this.conflicts())
 
329
        this.auto_resolve()
 
330
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
 
331
                         this.conflicts())
 
332
        self.build_tree_contents([('this/hello', '<<<<<<<')])
 
333
        this.auto_resolve()
 
334
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
 
335
                         this.conflicts())
 
336
        self.build_tree_contents([('this/hello', '=======')])
 
337
        this.auto_resolve()
 
338
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
 
339
                         this.conflicts())
 
340
        self.build_tree_contents([('this/hello', '\n>>>>>>>')])
 
341
        remaining, resolved = this.auto_resolve()
 
342
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
 
343
                         this.conflicts())
 
344
        self.assertEqual([], resolved)
 
345
        self.build_tree_contents([('this/hello', 'hELLO wORLD')])
 
346
        remaining, resolved = this.auto_resolve()
 
347
        self.assertEqual([], this.conflicts())
 
348
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
 
349
                         resolved)
 
350
        self.failIfExists('this/hello.BASE')
 
351
 
 
352
    def test_auto_resolve_dir(self):
 
353
        tree = self.make_branch_and_tree('tree')
 
354
        self.build_tree(['tree/hello/'])
 
355
        tree.add('hello', 'hello-id')
 
356
        file_conflict = conflicts.TextConflict('file', None, 'hello-id')
 
357
        tree.set_conflicts(conflicts.ConflictList([file_conflict]))
 
358
        tree.auto_resolve()