~bzr-pqm/bzr/bzr.dev

1553.5.74 by Martin Pool
Convert WorkingTree format3 to use LockDirs
1
# Copyright (C) 2005, 2006 Canonical Ltd
1399.1.12 by Robert Collins
add new test script
2
# Authors:  Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
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
17
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
18
from cStringIO import StringIO
1399.1.12 by Robert Collins
add new test script
19
import os
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
20
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
21
from bzrlib import (
22
    bzrdir,
23
    conflicts,
24
    errors,
25
    workingtree,
26
    )
1399.1.12 by Robert Collins
add new test script
27
from bzrlib.branch import Branch
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
28
from bzrlib.bzrdir import BzrDir
1553.5.74 by Martin Pool
Convert WorkingTree format3 to use LockDirs
29
from bzrlib.lockdir import LockDir
1986.1.8 by Robert Collins
Update to bzr.dev, which involves adding lock_tree_write to MutableTree and MemoryTree.
30
from bzrlib.mutabletree import needs_tree_write_lock
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
31
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
1534.4.46 by Robert Collins
Nearly complete .bzr/checkout splitout.
32
from bzrlib.transport import get_transport
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
33
from bzrlib.workingtree import (
34
    TreeEntry,
35
    TreeDirectory,
36
    TreeFile,
37
    TreeLink,
38
    )
1399.1.12 by Robert Collins
add new test script
39
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
40
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
41
class TestTreeDirectory(TestCaseWithTransport):
1399.1.12 by Robert Collins
add new test script
42
43
    def test_kind_character(self):
44
        self.assertEqual(TreeDirectory().kind_character(), '/')
45
46
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
47
class TestTreeEntry(TestCaseWithTransport):
1399.1.12 by Robert Collins
add new test script
48
49
    def test_kind_character(self):
50
        self.assertEqual(TreeEntry().kind_character(), '???')
51
52
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
53
class TestTreeFile(TestCaseWithTransport):
1399.1.12 by Robert Collins
add new test script
54
55
    def test_kind_character(self):
56
        self.assertEqual(TreeFile().kind_character(), '')
57
58
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
59
class TestTreeLink(TestCaseWithTransport):
1399.1.12 by Robert Collins
add new test script
60
61
    def test_kind_character(self):
62
        self.assertEqual(TreeLink().kind_character(), '')
63
64
1534.4.46 by Robert Collins
Nearly complete .bzr/checkout splitout.
65
class TestDefaultFormat(TestCaseWithTransport):
66
67
    def test_get_set_default_format(self):
68
        old_format = workingtree.WorkingTreeFormat.get_default_format()
69
        # default is 3
70
        self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
71
        workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
72
        try:
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()
77
            dir.create_branch()
78
            result = dir.create_workingtree()
79
            self.assertEqual(result, 'A tree')
80
        finally:
81
            workingtree.WorkingTreeFormat.set_default_format(old_format)
82
        self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
83
3753.1.1 by John Arbash Meinel
Add some simple direct tests for WT.open and WT.open_containing.
84
    def test_open(self):
85
        tree = self.make_branch_and_tree('.')
3753.1.2 by John Arbash Meinel
Switch to using the class attribute, rather than the instance
86
        open_direct = workingtree.WorkingTree.open('.')
3753.1.1 by John Arbash Meinel
Add some simple direct tests for WT.open and WT.open_containing.
87
        self.assertEqual(tree.basedir, open_direct.basedir)
3753.1.2 by John Arbash Meinel
Switch to using the class attribute, rather than the instance
88
        open_no_args = workingtree.WorkingTree.open()
3753.1.1 by John Arbash Meinel
Add some simple direct tests for WT.open and WT.open_containing.
89
        self.assertEqual(tree.basedir, open_no_args.basedir)
90
91
    def test_open_containing(self):
92
        tree = self.make_branch_and_tree('.')
3753.1.2 by John Arbash Meinel
Switch to using the class attribute, rather than the instance
93
        open_direct, relpath = workingtree.WorkingTree.open_containing('.')
3753.1.1 by John Arbash Meinel
Add some simple direct tests for WT.open and WT.open_containing.
94
        self.assertEqual(tree.basedir, open_direct.basedir)
95
        self.assertEqual('', relpath)
3753.1.2 by John Arbash Meinel
Switch to using the class attribute, rather than the instance
96
        open_no_args, relpath = workingtree.WorkingTree.open_containing()
3753.1.1 by John Arbash Meinel
Add some simple direct tests for WT.open and WT.open_containing.
97
        self.assertEqual(tree.basedir, open_no_args.basedir)
98
        self.assertEqual('', relpath)
3753.1.2 by John Arbash Meinel
Switch to using the class attribute, rather than the instance
99
        open_subdir, relpath = workingtree.WorkingTree.open_containing('subdir')
3753.1.1 by John Arbash Meinel
Add some simple direct tests for WT.open and WT.open_containing.
100
        self.assertEqual(tree.basedir, open_subdir.basedir)
101
        self.assertEqual('subdir', relpath)
102
1534.4.46 by Robert Collins
Nearly complete .bzr/checkout splitout.
103
104
class SampleTreeFormat(workingtree.WorkingTreeFormat):
105
    """A sample format
106
107
    this format is initializable, unsupported to aid in testing the 
108
    open and open_downlevel routines.
109
    """
110
111
    def get_format_string(self):
112
        """See WorkingTreeFormat.get_format_string()."""
113
        return "Sample tree format."
114
3123.5.3 by Aaron Bentley
Get tests passing with accelerator_tree
115
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3136.1.5 by Aaron Bentley
Fix sample workingtree format
116
                   accelerator_tree=None, hardlink=False):
1534.4.46 by Robert Collins
Nearly complete .bzr/checkout splitout.
117
        """Sample branches cannot be created."""
118
        t = a_bzrdir.get_workingtree_transport(self)
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
119
        t.put_bytes('format', self.get_format_string())
1534.4.46 by Robert Collins
Nearly complete .bzr/checkout splitout.
120
        return 'A tree'
121
122
    def is_supported(self):
123
        return False
124
125
    def open(self, transport, _found=False):
126
        return "opened tree."
127
128
129
class TestWorkingTreeFormat(TestCaseWithTransport):
130
    """Tests for the WorkingTreeFormat facility."""
131
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()
139
            dir.create_branch()
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")
145
        
146
    def test_find_format_no_tree(self):
147
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
148
        self.assertRaises(errors.NoWorkingTree,
1534.4.46 by Robert Collins
Nearly complete .bzr/checkout splitout.
149
                          workingtree.WorkingTreeFormat.find_format,
150
                          dir)
151
152
    def test_find_format_unknown_format(self):
153
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
154
        dir.create_repository()
155
        dir.create_branch()
156
        SampleTreeFormat().initialize(dir)
157
        self.assertRaises(errors.UnknownFormatError,
158
                          workingtree.WorkingTreeFormat.find_format,
159
                          dir)
160
161
    def test_register_unregister_format(self):
162
        format = SampleTreeFormat()
163
        # make a control dir
164
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
165
        dir.create_repository()
166
        dir.create_branch()
167
        # make a branch
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)
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
177
178
179
class TestWorkingTreeFormat3(TestCaseWithTransport):
180
    """Tests specific to WorkingTreeFormat3."""
181
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)
187
        # we want:
188
        # format 'Bazaar-NG Working Tree format 3'
189
        # inventory = blank inventory
190
        # pending-merges = ''
191
        # stat-cache = ??
192
        # no inventory.basis yet
193
        t = control.get_workingtree_transport(None)
1553.5.81 by Martin Pool
Revert change to WorkingTreeFormat3 format string; too many things want it the old way
194
        self.assertEqualDiff('Bazaar-NG Working Tree format 3',
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
195
                             t.get('format').read())
2100.3.12 by Aaron Bentley
Stop generating unique roots for WorkingTree3
196
        self.assertEqualDiff(t.get('inventory').read(), 
197
                              '<inventory format="5">\n'
1731.1.33 by Aaron Bentley
Revert no-special-root changes
198
                              '</inventory>\n',
199
                             )
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
200
        self.assertEqualDiff('### bzr hashcache v5\n',
201
                             t.get('stat-cache').read())
202
        self.assertFalse(t.has('inventory.basis'))
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
203
        # no last-revision file means 'None' or 'NULLREVISION'
204
        self.assertFalse(t.has('last-revision'))
1534.4.51 by Robert Collins
Test the disk layout of format3 working trees.
205
        # TODO RBC 20060210 do a commit, check the inventory.basis is created 
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
206
        # correctly and last-revision file becomes present.
1553.5.74 by Martin Pool
Convert WorkingTree format3 to use LockDirs
207
208
    def test_uses_lockdir(self):
209
        """WorkingTreeFormat3 uses its own LockDir:
210
            
211
            - lock is a directory
212
            - when the WorkingTree is locked, LockDir can see that
213
        """
214
        t = self.get_transport()
215
        url = self.get_url()
216
        dir = bzrdir.BzrDirMetaFormat1().initialize(url)
217
        repo = dir.create_repository()
218
        branch = dir.create_branch()
1558.10.1 by Aaron Bentley
Handle lockdirs over NFS properly
219
        try:
220
            tree = workingtree.WorkingTreeFormat3().initialize(dir)
221
        except errors.NotLocalUrl:
222
            raise TestSkipped('Not a local URL')
1553.5.74 by Martin Pool
Convert WorkingTree format3 to use LockDirs
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)
1553.5.75 by Martin Pool
Additional WorkingTree LockDir test
228
        tree.lock_write()
229
        self.assertTrue(our_lock.peek())
230
        tree.unlock()
231
        self.assertEquals(our_lock.peek(), None)
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
232
1815.2.2 by Jelmer Vernooij
Move missing_pending_merges test to WorkingTreeFormat3-specific tests.
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)
3407.2.14 by Martin Pool
Remove more cases of getting transport via control_files
238
        tree._transport.delete("pending-merges")
1908.6.11 by Robert Collins
Remove usage of tree.pending_merges().
239
        self.assertEqual([], tree.get_parent_ids())
1815.2.2 by Jelmer Vernooij
Move missing_pending_merges test to WorkingTreeFormat3-specific tests.
240
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
241
242
class TestFormat2WorkingTree(TestCaseWithTransport):
243
    """Tests that are specific to format 2 trees."""
244
1534.10.8 by Aaron Bentley
Implemented conflict_lines in terms of old system on WorkingTree
245
    def create_format2_tree(self, url):
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
246
        return self.make_branch_and_tree(
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
247
            url, format=bzrdir.BzrDirFormat6())
1534.10.6 by Aaron Bentley
Conflict serialization working for WorkingTree3
248
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
249
    def test_conflicts(self):
1534.10.8 by Aaron Bentley
Implemented conflict_lines in terms of old system on WorkingTree
250
        # test backwards compatability
251
        tree = self.create_format2_tree('.')
1534.10.22 by Aaron Bentley
Got ConflictList implemented
252
        self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
1534.10.8 by Aaron Bentley
Implemented conflict_lines in terms of old system on WorkingTree
253
                          None)
254
        file('lala.BASE', 'wb').write('labase')
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
255
        expected = conflicts.ContentsConflict('lala')
1534.10.22 by Aaron Bentley
Got ConflictList implemented
256
        self.assertEqual(list(tree.conflicts()), [expected])
1534.10.8 by Aaron Bentley
Implemented conflict_lines in terms of old system on WorkingTree
257
        file('lala', 'wb').write('la')
258
        tree.add('lala', 'lala-id')
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
259
        expected = conflicts.ContentsConflict('lala', file_id='lala-id')
1534.10.22 by Aaron Bentley
Got ConflictList implemented
260
        self.assertEqual(list(tree.conflicts()), [expected])
1534.10.8 by Aaron Bentley
Implemented conflict_lines in terms of old system on WorkingTree
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
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
264
        expected = conflicts.TextConflict('lala', file_id='lala-id')
1534.10.22 by Aaron Bentley
Got ConflictList implemented
265
        self.assertEqual(list(tree.conflicts()), [expected])
1534.10.8 by Aaron Bentley
Implemented conflict_lines in terms of old system on WorkingTree
266
        os.unlink('lala.OTHER')
267
        os.mkdir('lala.OTHER')
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
268
        expected = conflicts.ContentsConflict('lala', file_id='lala-id')
1534.10.22 by Aaron Bentley
Got ConflictList implemented
269
        self.assertEqual(list(tree.conflicts()), [expected])
1713.2.3 by Robert Collins
Combine ignore rules into a single regex preventing pathological behaviour during add.
270
271
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
272
class InstrumentedTree(object):
273
    """A instrumented tree to check the needs_tree_write_lock decorator."""
274
275
    def __init__(self):
276
        self._locks = []
277
278
    def lock_tree_write(self):
279
        self._locks.append('t')
280
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."""
284
        return args, kwargs
285
286
    @needs_tree_write_lock
287
    def method_that_raises(self):
288
        """This method causes an exception when called with parameters.
289
        
290
        This allows the decorator code to be checked - it should still call
291
        unlock.
292
        """
293
294
    def unlock(self):
295
        self._locks.append('u')
296
297
298
class TestInstrumentedTree(TestCase):
299
300
    def test_needs_tree_write_lock(self):
301
        """@needs_tree_write_lock should be semantically transparent."""
302
        tree = InstrumentedTree()
303
        self.assertEqual(
304
            'method_with_tree_write_lock',
305
            tree.method_with_tree_write_lock.__name__)
306
        self.assertEqual(
307
            "A lock_tree_write decorated method that returns its arguments.",
308
            tree.method_with_tree_write_lock.__doc__)
309
        args = (1, 2, 3)
310
        kwargs = {'a':'b'}
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)
2120.7.2 by Aaron Bentley
Move autoresolve functionality to workingtree
316
317
3017.2.1 by Aaron Bentley
Revert now resolves conflicts recursively (#102739)
318
class TestRevert(TestCaseWithTransport):
319
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()))
335
336
2120.7.2 by Aaron Bentley
Move autoresolve functionality to workingtree
337
class TestAutoResolve(TestCaseWithTransport):
338
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')
343
        base.commit('Hello')
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')],
353
                         this.conflicts())
354
        this.auto_resolve()
355
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
356
                         this.conflicts())
357
        self.build_tree_contents([('this/hello', '<<<<<<<')])
358
        this.auto_resolve()
359
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
360
                         this.conflicts())
361
        self.build_tree_contents([('this/hello', '=======')])
362
        this.auto_resolve()
363
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
364
                         this.conflicts())
365
        self.build_tree_contents([('this/hello', '\n>>>>>>>')])
366
        remaining, resolved = this.auto_resolve()
367
        self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
368
                         this.conflicts())
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')],
374
                         resolved)
375
        self.failIfExists('this/hello.BASE')
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
376
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')
2255.2.234 by Robert Collins
Merge bzr.dev.
381
        file_conflict = conflicts.TextConflict('file', None, 'hello-id')
2120.7.3 by Aaron Bentley
Update resolve command to automatically mark conflicts as resolved
382
        tree.set_conflicts(conflicts.ConflictList([file_conflict]))
383
        tree.auto_resolve()
3140.1.4 by Aaron Bentley
Add WorkingTree.find_trees
384
385
386
class TestFindTrees(TestCaseWithTransport):
387
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)))