~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_workingtree.py

  • Committer: Jelmer Vernooij
  • Date: 2011-08-19 22:34:02 UTC
  • mto: This revision was merged to the branch mainline in revision 6089.
  • Revision ID: jelmer@samba.org-20110819223402-wjywqb0fa1xxx522
Use get_transport_from_{url,path} in more places.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
# Authors:  Robert Collins <robert.collins@canonical.com>
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
13
13
#
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
 
18
 
from cStringIO import StringIO
19
 
import os
20
 
 
21
 
from bzrlib import dirstate, ignores
22
 
import bzrlib
23
 
from bzrlib.branch import Branch
24
 
from bzrlib import bzrdir, conflicts, errors, workingtree
25
 
from bzrlib.bzrdir import BzrDir
26
 
from bzrlib.errors import NotBranchError, NotVersionedError
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
 
 
18
from bzrlib import (
 
19
    bzrdir,
 
20
    conflicts,
 
21
    errors,
 
22
    symbol_versioning,
 
23
    transport,
 
24
    workingtree,
 
25
    workingtree_3,
 
26
    workingtree_4,
 
27
    )
27
28
from bzrlib.lockdir import LockDir
28
29
from bzrlib.mutabletree import needs_tree_write_lock
29
 
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
 
from bzrlib.symbol_versioning import zero_thirteen
31
30
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
32
 
from bzrlib.trace import mutter
33
 
from bzrlib.transport import get_transport
34
31
from bzrlib.workingtree import (
35
32
    TreeEntry,
36
33
    TreeDirectory,
37
34
    TreeFile,
38
35
    TreeLink,
39
 
    WorkingTree,
40
36
    )
41
37
 
 
38
 
42
39
class TestTreeDirectory(TestCaseWithTransport):
43
40
 
44
41
    def test_kind_character(self):
66
63
class TestDefaultFormat(TestCaseWithTransport):
67
64
 
68
65
    def test_get_set_default_format(self):
69
 
        old_format = workingtree.WorkingTreeFormat.get_default_format()
70
 
        # default is 3
71
 
        self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
72
 
        workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
73
 
        try:
74
 
            # the default branch format is used by the meta dir format
75
 
            # which is not the default bzrdir format at this point
76
 
            dir = bzrdir.BzrDirMetaFormat1().initialize('.')
77
 
            dir.create_repository()
78
 
            dir.create_branch()
79
 
            result = dir.create_workingtree()
80
 
            self.assertEqual(result, 'A tree')
81
 
        finally:
82
 
            workingtree.WorkingTreeFormat.set_default_format(old_format)
83
 
        self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
 
66
        old_format = workingtree.format_registry.get_default()
 
67
        # default is 6
 
68
        self.assertTrue(isinstance(old_format, workingtree_4.WorkingTreeFormat6))
 
69
        workingtree.format_registry.set_default(SampleTreeFormat())
 
70
        try:
 
71
            # the default branch format is used by the meta dir format
 
72
            # which is not the default bzrdir format at this point
 
73
            dir = bzrdir.BzrDirMetaFormat1().initialize('.')
 
74
            dir.create_repository()
 
75
            dir.create_branch()
 
76
            result = dir.create_workingtree()
 
77
            self.assertEqual(result, 'A tree')
 
78
        finally:
 
79
            workingtree.format_registry.set_default(old_format)
 
80
        self.assertEqual(old_format, workingtree.format_registry.get_default())
 
81
 
 
82
    def test_get_set_default_format_by_key(self):
 
83
        old_format = workingtree.format_registry.get_default()
 
84
        # default is 6
 
85
        format = SampleTreeFormat()
 
86
        workingtree.format_registry.register(format)
 
87
        self.addCleanup(workingtree.format_registry.remove, format)
 
88
        self.assertTrue(isinstance(old_format, workingtree_4.WorkingTreeFormat6))
 
89
        workingtree.format_registry.set_default_key(format.get_format_string())
 
90
        try:
 
91
            # the default branch format is used by the meta dir format
 
92
            # which is not the default bzrdir format at this point
 
93
            dir = bzrdir.BzrDirMetaFormat1().initialize('.')
 
94
            dir.create_repository()
 
95
            dir.create_branch()
 
96
            result = dir.create_workingtree()
 
97
            self.assertEqual(result, 'A tree')
 
98
        finally:
 
99
            workingtree.format_registry.set_default_key(
 
100
                old_format.get_format_string())
 
101
        self.assertEqual(old_format, workingtree.format_registry.get_default())
 
102
 
 
103
    def test_open(self):
 
104
        tree = self.make_branch_and_tree('.')
 
105
        open_direct = workingtree.WorkingTree.open('.')
 
106
        self.assertEqual(tree.basedir, open_direct.basedir)
 
107
        open_no_args = workingtree.WorkingTree.open()
 
108
        self.assertEqual(tree.basedir, open_no_args.basedir)
 
109
 
 
110
    def test_open_containing(self):
 
111
        tree = self.make_branch_and_tree('.')
 
112
        open_direct, relpath = workingtree.WorkingTree.open_containing('.')
 
113
        self.assertEqual(tree.basedir, open_direct.basedir)
 
114
        self.assertEqual('', relpath)
 
115
        open_no_args, relpath = workingtree.WorkingTree.open_containing()
 
116
        self.assertEqual(tree.basedir, open_no_args.basedir)
 
117
        self.assertEqual('', relpath)
 
118
        open_subdir, relpath = workingtree.WorkingTree.open_containing('subdir')
 
119
        self.assertEqual(tree.basedir, open_subdir.basedir)
 
120
        self.assertEqual('subdir', relpath)
84
121
 
85
122
 
86
123
class SampleTreeFormat(workingtree.WorkingTreeFormat):
87
124
    """A sample format
88
125
 
89
 
    this format is initializable, unsupported to aid in testing the 
 
126
    this format is initializable, unsupported to aid in testing the
90
127
    open and open_downlevel routines.
91
128
    """
92
129
 
94
131
        """See WorkingTreeFormat.get_format_string()."""
95
132
        return "Sample tree format."
96
133
 
97
 
    def initialize(self, a_bzrdir, revision_id=None):
 
134
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
135
                   accelerator_tree=None, hardlink=False):
98
136
        """Sample branches cannot be created."""
99
137
        t = a_bzrdir.get_workingtree_transport(self)
100
138
        t.put_bytes('format', self.get_format_string())
107
145
        return "opened tree."
108
146
 
109
147
 
 
148
class SampleExtraTreeFormat(workingtree.WorkingTreeFormat):
 
149
    """A sample format that does not support use in a metadir.
 
150
 
 
151
    """
 
152
 
 
153
    def get_format_string(self):
 
154
        # Not usable in a metadir, so no format string
 
155
        return None
 
156
 
 
157
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
158
                   accelerator_tree=None, hardlink=False):
 
159
        raise NotImplementedError(self.initialize)
 
160
 
 
161
    def is_supported(self):
 
162
        return False
 
163
 
 
164
    def open(self, transport, _found=False):
 
165
        raise NotImplementedError(self.open)
 
166
 
 
167
 
110
168
class TestWorkingTreeFormat(TestCaseWithTransport):
111
169
    """Tests for the WorkingTreeFormat facility."""
112
170
 
 
171
    def test_find_format_string(self):
 
172
        # is the right format object found for a working tree?
 
173
        branch = self.make_branch('branch')
 
174
        self.assertRaises(errors.NoWorkingTree,
 
175
            workingtree.WorkingTreeFormat.find_format_string, branch.bzrdir)
 
176
        transport = branch.bzrdir.get_workingtree_transport(None)
 
177
        transport.mkdir('.')
 
178
        transport.put_bytes("format", "some format name")
 
179
        # The format does not have to be known by Bazaar,
 
180
        # find_format_string just retrieves the name
 
181
        self.assertEquals("some format name",
 
182
            workingtree.WorkingTreeFormat.find_format_string(branch.bzrdir))
 
183
 
113
184
    def test_find_format(self):
114
185
        # is the right format object found for a working tree?
115
186
        # create a branch with a few known format objects.
119
190
            dir.create_repository()
120
191
            dir.create_branch()
121
192
            format.initialize(dir)
122
 
            t = get_transport(url)
 
193
            t = transport.get_transport(url)
123
194
            found_format = workingtree.WorkingTreeFormat.find_format(dir)
124
 
            self.failUnless(isinstance(found_format, format.__class__))
125
 
        check_format(workingtree.WorkingTreeFormat3(), "bar")
126
 
        
 
195
            self.assertIsInstance(found_format, format.__class__)
 
196
        check_format(workingtree_3.WorkingTreeFormat3(), "bar")
 
197
 
127
198
    def test_find_format_no_tree(self):
128
199
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
129
200
        self.assertRaises(errors.NoWorkingTree,
148
219
        # make a branch
149
220
        format.initialize(dir)
150
221
        # register a format for it.
151
 
        workingtree.WorkingTreeFormat.register_format(format)
 
222
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
223
            workingtree.WorkingTreeFormat.register_format, format)
 
224
        self.assertTrue(format in 
 
225
            self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
226
                workingtree.WorkingTreeFormat.get_formats))
152
227
        # which branch.Open will refuse (not supported)
153
228
        self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
154
229
        # but open_downlevel will work
155
230
        self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
156
231
        # unregister the format
157
 
        workingtree.WorkingTreeFormat.unregister_format(format)
 
232
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
233
            workingtree.WorkingTreeFormat.unregister_format, format)
 
234
        self.assertFalse(format in
 
235
            self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
236
                workingtree.WorkingTreeFormat.get_formats))
 
237
 
 
238
 
 
239
class TestWorkingTreeIterEntriesByDir_wSubtrees(TestCaseWithTransport):
 
240
 
 
241
    def make_simple_tree(self):
 
242
        tree = self.make_branch_and_tree('tree', format='development-subtree')
 
243
        self.build_tree(['tree/a/', 'tree/a/b/', 'tree/a/b/c'])
 
244
        tree.set_root_id('root-id')
 
245
        tree.add(['a', 'a/b', 'a/b/c'], ['a-id', 'b-id', 'c-id'])
 
246
        tree.commit('initial')
 
247
        return tree
 
248
 
 
249
    def test_just_directory(self):
 
250
        tree = self.make_simple_tree()
 
251
        self.assertEqual([('directory', 'root-id'),
 
252
                          ('directory', 'a-id'),
 
253
                          ('directory', 'b-id'),
 
254
                          ('file', 'c-id')],
 
255
                         [(ie.kind, ie.file_id)
 
256
                          for path, ie in tree.iter_entries_by_dir()])
 
257
        subtree = self.make_branch_and_tree('tree/a/b')
 
258
        self.assertEqual([('tree-reference', 'b-id')],
 
259
                         [(ie.kind, ie.file_id)
 
260
                          for path, ie in tree.iter_entries_by_dir(['b-id'])])
 
261
 
 
262
    def test_direct_subtree(self):
 
263
        tree = self.make_simple_tree()
 
264
        subtree = self.make_branch_and_tree('tree/a/b')
 
265
        self.assertEqual([('directory', 'root-id'),
 
266
                          ('directory', 'a-id'),
 
267
                          ('tree-reference', 'b-id')],
 
268
                         [(ie.kind, ie.file_id)
 
269
                          for path, ie in tree.iter_entries_by_dir()])
 
270
 
 
271
    def test_indirect_subtree(self):
 
272
        tree = self.make_simple_tree()
 
273
        subtree = self.make_branch_and_tree('tree/a')
 
274
        self.assertEqual([('directory', 'root-id'),
 
275
                          ('tree-reference', 'a-id')],
 
276
                         [(ie.kind, ie.file_id)
 
277
                          for path, ie in tree.iter_entries_by_dir()])
 
278
 
 
279
 
 
280
class TestWorkingTreeFormatRegistry(TestCase):
 
281
 
 
282
    def setUp(self):
 
283
        super(TestWorkingTreeFormatRegistry, self).setUp()
 
284
        self.registry = workingtree.WorkingTreeFormatRegistry()
 
285
 
 
286
    def test_register_unregister_format(self):
 
287
        format = SampleTreeFormat()
 
288
        self.registry.register(format)
 
289
        self.assertEquals(format, self.registry.get("Sample tree format."))
 
290
        self.registry.remove(format)
 
291
        self.assertRaises(KeyError, self.registry.get, "Sample tree format.")
 
292
 
 
293
    def test_get_all(self):
 
294
        format = SampleTreeFormat()
 
295
        self.assertEquals([], self.registry._get_all())
 
296
        self.registry.register(format)
 
297
        self.assertEquals([format], self.registry._get_all())
 
298
 
 
299
    def test_register_extra(self):
 
300
        format = SampleExtraTreeFormat()
 
301
        self.assertEquals([], self.registry._get_all())
 
302
        self.registry.register_extra(format)
 
303
        self.assertEquals([format], self.registry._get_all())
 
304
 
 
305
    def test_register_extra_lazy(self):
 
306
        self.assertEquals([], self.registry._get_all())
 
307
        self.registry.register_extra_lazy("bzrlib.tests.test_workingtree",
 
308
            "SampleExtraTreeFormat")
 
309
        formats = self.registry._get_all()
 
310
        self.assertEquals(1, len(formats))
 
311
        self.assertIsInstance(formats[0], SampleExtraTreeFormat)
158
312
 
159
313
 
160
314
class TestWorkingTreeFormat3(TestCaseWithTransport):
164
318
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
165
319
        control.create_repository()
166
320
        control.create_branch()
167
 
        tree = workingtree.WorkingTreeFormat3().initialize(control)
 
321
        tree = workingtree_3.WorkingTreeFormat3().initialize(control)
168
322
        # we want:
169
323
        # format 'Bazaar-NG Working Tree format 3'
170
324
        # inventory = blank inventory
174
328
        t = control.get_workingtree_transport(None)
175
329
        self.assertEqualDiff('Bazaar-NG Working Tree format 3',
176
330
                             t.get('format').read())
177
 
        # self.assertContainsRe(t.get('inventory').read(), 
178
 
        #                       '<inventory file_id="[^"]*" format="5">\n'
179
 
        #                       '</inventory>\n',
180
 
        #                      )
181
 
        # WorkingTreeFormat3 doesn't default to creating a unique root id,
182
 
        # because it is incompatible with older bzr versions
183
 
        self.assertContainsRe(t.get('inventory').read(),
 
331
        self.assertEqualDiff(t.get('inventory').read(),
184
332
                              '<inventory format="5">\n'
185
333
                              '</inventory>\n',
186
334
                             )
189
337
        self.assertFalse(t.has('inventory.basis'))
190
338
        # no last-revision file means 'None' or 'NULLREVISION'
191
339
        self.assertFalse(t.has('last-revision'))
192
 
        # TODO RBC 20060210 do a commit, check the inventory.basis is created 
 
340
        # TODO RBC 20060210 do a commit, check the inventory.basis is created
193
341
        # correctly and last-revision file becomes present.
194
342
 
195
343
    def test_uses_lockdir(self):
196
344
        """WorkingTreeFormat3 uses its own LockDir:
197
 
            
 
345
 
198
346
            - lock is a directory
199
347
            - when the WorkingTree is locked, LockDir can see that
200
348
        """
204
352
        repo = dir.create_repository()
205
353
        branch = dir.create_branch()
206
354
        try:
207
 
            tree = workingtree.WorkingTreeFormat3().initialize(dir)
 
355
            tree = workingtree_3.WorkingTreeFormat3().initialize(dir)
208
356
        except errors.NotLocalUrl:
209
357
            raise TestSkipped('Not a local URL')
210
358
        self.assertIsDirectory('.bzr', t)
221
369
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
222
370
        control.create_repository()
223
371
        control.create_branch()
224
 
        tree = workingtree.WorkingTreeFormat3().initialize(control)
225
 
        tree._control_files._transport.delete("pending-merges")
 
372
        tree = workingtree_3.WorkingTreeFormat3().initialize(control)
 
373
        tree._transport.delete("pending-merges")
226
374
        self.assertEqual([], tree.get_parent_ids())
227
375
 
228
376
 
229
 
class TestWorkingTreeFormat4(TestCaseWithTransport):
230
 
    """Tests specific to WorkingTreeFormat4."""
231
 
 
232
 
    def test_disk_layout(self):
233
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
234
 
        control.create_repository()
235
 
        control.create_branch()
236
 
        tree = workingtree.WorkingTreeFormat4().initialize(control)
237
 
        # we want:
238
 
        # format 'Bazaar Working Tree format 4'
239
 
        # stat-cache = ??
240
 
        t = control.get_workingtree_transport(None)
241
 
        self.assertEqualDiff('Bazaar Working Tree format 4\n',
242
 
                             t.get('format').read())
243
 
        self.assertEqualDiff('### bzr hashcache v5\n',
244
 
                             t.get('stat-cache').read())
245
 
        self.assertFalse(t.has('inventory.basis'))
246
 
        # no last-revision file means 'None' or 'NULLREVISION'
247
 
        self.assertFalse(t.has('last-revision'))
248
 
        # TODO RBC 20060210 do a commit, check the inventory.basis is created 
249
 
        # correctly and last-revision file becomes present.
250
 
        # manually make a dirstate toc check the format is as desired.
251
 
        state = dirstate.DirState.on_file(t.local_abspath('dirstate'))
252
 
        self.assertEqual([], state.get_parent_ids())
253
 
 
254
 
    def test_uses_lockdir(self):
255
 
        """WorkingTreeFormat4 uses its own LockDir:
256
 
            
257
 
            - lock is a directory
258
 
            - when the WorkingTree is locked, LockDir can see that
259
 
        """
260
 
        # this test could be factored into a subclass of tests common to both
261
 
        # format 3 and 4, but for now its not much of an issue as there is only one in common.
262
 
        t = self.get_transport()
263
 
        tree = self.make_workingtree()
264
 
        self.assertIsDirectory('.bzr', t)
265
 
        self.assertIsDirectory('.bzr/checkout', t)
266
 
        self.assertIsDirectory('.bzr/checkout/lock', t)
267
 
        our_lock = LockDir(t, '.bzr/checkout/lock')
268
 
        self.assertEquals(our_lock.peek(), None)
269
 
        tree.lock_write()
270
 
        self.assertTrue(our_lock.peek())
271
 
        tree.unlock()
272
 
        self.assertEquals(our_lock.peek(), None)
273
 
 
274
 
    def make_workingtree(self):
275
 
        url = self.get_url()
276
 
        dir = bzrdir.BzrDirMetaFormat1().initialize(url)
277
 
        repo = dir.create_repository()
278
 
        branch = dir.create_branch()
279
 
        try:
280
 
            return workingtree.WorkingTreeFormat4().initialize(dir)
281
 
        except errors.NotLocalUrl:
282
 
            raise TestSkipped('Not a local URL')
283
 
 
284
 
    # TODO: test that dirstate also stores & retrieves the parent list of 
285
 
    # workingtree-parent revisions, including when they have multiple parents.
286
 
    # (in other words, the case when we're constructing a merge of 
287
 
    # revisions which are themselves merges.)
288
 
 
289
 
    # The simplest case is that the the workingtree's primary 
290
 
    # parent tree can be retrieved.  This is required for all WorkingTrees, 
291
 
    # and covered by the generic tests.
292
 
 
293
 
    def test_dirstate_stores_all_parent_inventories(self):
294
 
        tree = self.make_workingtree()
295
 
 
296
 
        # We're going to build in tree a working tree 
297
 
        # with three parent trees, with some files in common.  
298
 
    
299
 
        # We really don't want to do commit or merge in the new dirstate-based
300
 
        # tree, because that might not work yet.  So instead we build
301
 
        # revisions elsewhere and pull them across, doing by hand part of the
302
 
        # work that merge would do.
303
 
 
304
 
        subtree = self.make_branch_and_tree('subdir')
305
 
        # writelock the tree so its repository doesn't get readlocked by
306
 
        # the revision tree locks. This works around the bug where we dont
307
 
        # permit lock upgrading.
308
 
        subtree.lock_write()
309
 
        self.addCleanup(subtree.unlock)
310
 
        self.build_tree(['subdir/file-a',])
311
 
        subtree.add(['file-a'], ['id-a'])
312
 
        rev1 = subtree.commit('commit in subdir')
313
 
        rev1_tree = subtree.basis_tree()
314
 
        rev1_tree.lock_read()
315
 
        self.addCleanup(rev1_tree.unlock)
316
 
 
317
 
        subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
318
 
        self.build_tree(['subdir2/file-b'])
319
 
        subtree2.add(['file-b'], ['id-b'])
320
 
        rev2 = subtree2.commit('commit in subdir2')
321
 
        rev2_tree = subtree2.basis_tree()
322
 
        rev2_tree.lock_read()
323
 
        self.addCleanup(rev2_tree.unlock)
324
 
 
325
 
        subtree.merge_from_branch(subtree2.branch)
326
 
        rev3 = subtree.commit('merge from subdir2')
327
 
        rev3_tree = subtree.basis_tree()
328
 
        rev3_tree.lock_read()
329
 
        self.addCleanup(rev3_tree.unlock)
330
 
 
331
 
        repo = tree.branch.repository
332
 
        repo.fetch(subtree.branch.repository, rev3)
333
 
        # will also pull the others...
334
 
 
335
 
        # tree doesn't contain a text merge yet but we'll just
336
 
        # set the parents as if a merge had taken place. 
337
 
        # this should cause the tree data to be folded into the 
338
 
        # dirstate.
339
 
        tree.set_parent_trees([
340
 
            (rev1, rev1_tree),
341
 
            (rev2, rev2_tree),
342
 
            (rev3, rev3_tree), ])
343
 
 
344
 
        # now we should be able to get them back out
345
 
        self.assertTreesEqual(tree.revision_tree(rev1), rev1_tree)
346
 
        self.assertTreesEqual(tree.revision_tree(rev2), rev2_tree)
347
 
        self.assertTreesEqual(tree.revision_tree(rev3), rev3_tree)
348
 
 
349
 
    def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
350
 
        """Setting parent trees on a dirstate working tree takes
351
 
        the trees it's given and doesn't need to read them from the 
352
 
        repository.
353
 
        """
354
 
        tree = self.make_workingtree()
355
 
 
356
 
        subtree = self.make_branch_and_tree('subdir')
357
 
        rev1 = subtree.commit('commit in subdir')
358
 
        rev1_tree = subtree.basis_tree()
359
 
        rev1_tree.lock_read()
360
 
        self.addCleanup(rev1_tree.unlock)
361
 
 
362
 
        tree.branch.pull(subtree.branch)
363
 
 
364
 
        # break the repository's legs to make sure it only uses the trees
365
 
        # it's given; any calls to forbidden methods will raise an 
366
 
        # AssertionError
367
 
        repo = tree.branch.repository
368
 
        repo.get_revision = self.fail
369
 
        repo.get_inventory = self.fail
370
 
        repo.get_inventory_xml = self.fail
371
 
        # try to set the parent trees.
372
 
        tree.set_parent_trees([(rev1, rev1_tree)])
373
 
 
374
 
    def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
375
 
        """Getting parent trees from a dirstate tree does not read from the 
376
 
        repos inventory store. This is an important part of the dirstate
377
 
        performance optimisation work.
378
 
        """
379
 
        tree = self.make_workingtree()
380
 
 
381
 
        subtree = self.make_branch_and_tree('subdir')
382
 
        # writelock the tree so its repository doesn't get readlocked by
383
 
        # the revision tree locks. This works around the bug where we dont
384
 
        # permit lock upgrading.
385
 
        subtree.lock_write()
386
 
        self.addCleanup(subtree.unlock)
387
 
        rev1 = subtree.commit('commit in subdir')
388
 
        rev1_tree = subtree.basis_tree()
389
 
        rev1_tree.lock_read()
390
 
        self.addCleanup(rev1_tree.unlock)
391
 
        rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
392
 
        rev2_tree = subtree.basis_tree()
393
 
        rev2_tree.lock_read()
394
 
        self.addCleanup(rev2_tree.unlock)
395
 
 
396
 
        tree.branch.pull(subtree.branch)
397
 
 
398
 
        # break the repository's legs to make sure it only uses the trees
399
 
        # it's given; any calls to forbidden methods will raise an 
400
 
        # AssertionError
401
 
        repo = tree.branch.repository
402
 
        # dont uncomment this: the revision object must be accessed to 
403
 
        # answer 'get_parent_ids' for the revision tree- dirstate does not 
404
 
        # cache the parents of a parent tree at this point.
405
 
        #repo.get_revision = self.fail
406
 
        repo.get_inventory = self.fail
407
 
        repo.get_inventory_xml = self.fail
408
 
        # set the parent trees.
409
 
        tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
410
 
        # read the first tree
411
 
        result_rev1_tree = tree.revision_tree(rev1)
412
 
        # read the second
413
 
        result_rev2_tree = tree.revision_tree(rev2)
414
 
        # compare - there should be no differences between the handed and 
415
 
        # returned trees
416
 
        self.assertTreesEqual(rev1_tree, result_rev1_tree)
417
 
        self.assertTreesEqual(rev2_tree, result_rev2_tree)
418
 
 
419
 
    def test_dirstate_doesnt_cache_non_parent_trees(self):
420
 
        """Getting parent trees from a dirstate tree does not read from the 
421
 
        repos inventory store. This is an important part of the dirstate
422
 
        performance optimisation work.
423
 
        """
424
 
        tree = self.make_workingtree()
425
 
 
426
 
        # make a tree that we can try for, which is able to be returned but
427
 
        # must not be
428
 
        subtree = self.make_branch_and_tree('subdir')
429
 
        rev1 = subtree.commit('commit in subdir')
430
 
        tree.branch.pull(subtree.branch)
431
 
        # check it fails
432
 
        self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
433
 
 
434
 
    def test_no_dirstate_outside_lock(self):
435
 
        # temporary test until the code is mature enough to test from outside.
436
 
        """Getting a dirstate object fails if there is no lock."""
437
 
        def lock_and_call_current_dirstate(tree, lock_method):
438
 
            getattr(tree, lock_method)()
439
 
            tree.current_dirstate()
440
 
            tree.unlock()
441
 
        tree = self.make_workingtree()
442
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
443
 
        lock_and_call_current_dirstate(tree, 'lock_read')
444
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
445
 
        lock_and_call_current_dirstate(tree, 'lock_write')
446
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
447
 
        lock_and_call_current_dirstate(tree, 'lock_tree_write')
448
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
449
 
 
450
 
    def test_new_dirstate_on_new_lock(self):
451
 
        # until we have detection for when a dirstate can be reused, we
452
 
        # want to reparse dirstate on every new lock.
453
 
        known_dirstates = set()
454
 
        def lock_and_compare_all_current_dirstate(tree, lock_method):
455
 
            getattr(tree, lock_method)()
456
 
            state = tree.current_dirstate()
457
 
            self.assertFalse(state in known_dirstates)
458
 
            known_dirstates.add(state)
459
 
            tree.unlock()
460
 
        tree = self.make_workingtree()
461
 
        # lock twice with each type to prevent silly per-lock-type bugs.
462
 
        # each lock and compare looks for a unique state object.
463
 
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
464
 
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
465
 
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
466
 
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
467
 
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
468
 
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
469
 
 
470
 
 
471
 
class TestFormat2WorkingTree(TestCaseWithTransport):
472
 
    """Tests that are specific to format 2 trees."""
473
 
 
474
 
    def create_format2_tree(self, url):
475
 
        return self.make_branch_and_tree(
476
 
            url, format=bzrlib.bzrdir.BzrDirFormat6())
477
 
 
478
 
    def test_conflicts(self):
479
 
        # test backwards compatability
480
 
        tree = self.create_format2_tree('.')
481
 
        self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
482
 
                          None)
483
 
        file('lala.BASE', 'wb').write('labase')
484
 
        expected = conflicts.ContentsConflict('lala')
485
 
        self.assertEqual(list(tree.conflicts()), [expected])
486
 
        file('lala', 'wb').write('la')
487
 
        tree.add('lala', 'lala-id')
488
 
        expected = conflicts.ContentsConflict('lala', file_id='lala-id')
489
 
        self.assertEqual(list(tree.conflicts()), [expected])
490
 
        file('lala.THIS', 'wb').write('lathis')
491
 
        file('lala.OTHER', 'wb').write('laother')
492
 
        # When "text conflict"s happen, stem, THIS and OTHER are text
493
 
        expected = conflicts.TextConflict('lala', file_id='lala-id')
494
 
        self.assertEqual(list(tree.conflicts()), [expected])
495
 
        os.unlink('lala.OTHER')
496
 
        os.mkdir('lala.OTHER')
497
 
        expected = conflicts.ContentsConflict('lala', file_id='lala-id')
498
 
        self.assertEqual(list(tree.conflicts()), [expected])
499
 
 
500
 
 
501
 
class TestNonFormatSpecificCode(TestCaseWithTransport):
502
 
    """This class contains tests of workingtree that are not format specific."""
503
 
 
504
 
    def test_gen_file_id(self):
505
 
        file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_file_id,
506
 
                                      'filename')
507
 
        self.assertStartsWith(file_id, 'filename-')
508
 
 
509
 
    def test_gen_root_id(self):
510
 
        file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_root_id)
511
 
        self.assertStartsWith(file_id, 'tree_root-')
512
 
        
513
 
 
514
377
class InstrumentedTree(object):
515
378
    """A instrumented tree to check the needs_tree_write_lock decorator."""
516
379
 
528
391
    @needs_tree_write_lock
529
392
    def method_that_raises(self):
530
393
        """This method causes an exception when called with parameters.
531
 
        
 
394
 
532
395
        This allows the decorator code to be checked - it should still call
533
396
        unlock.
534
397
        """
545
408
        self.assertEqual(
546
409
            'method_with_tree_write_lock',
547
410
            tree.method_with_tree_write_lock.__name__)
548
 
        self.assertEqual(
 
411
        self.assertDocstring(
549
412
            "A lock_tree_write decorated method that returns its arguments.",
550
 
            tree.method_with_tree_write_lock.__doc__)
 
413
            tree.method_with_tree_write_lock)
551
414
        args = (1, 2, 3)
552
415
        kwargs = {'a':'b'}
553
416
        result = tree.method_with_tree_write_lock(1,2,3, a='b')
555
418
        self.assertEqual(['t', 'u'], tree._locks)
556
419
        self.assertRaises(TypeError, tree.method_that_raises, 'foo')
557
420
        self.assertEqual(['t', 'u', 't', 'u'], tree._locks)
 
421
 
 
422
 
 
423
class TestRevert(TestCaseWithTransport):
 
424
 
 
425
    def test_revert_conflicts_recursive(self):
 
426
        this_tree = self.make_branch_and_tree('this-tree')
 
427
        self.build_tree_contents([('this-tree/foo/',),
 
428
                                  ('this-tree/foo/bar', 'bar')])
 
429
        this_tree.add(['foo', 'foo/bar'])
 
430
        this_tree.commit('created foo/bar')
 
431
        other_tree = this_tree.bzrdir.sprout('other-tree').open_workingtree()
 
432
        self.build_tree_contents([('other-tree/foo/bar', 'baz')])
 
433
        other_tree.commit('changed bar')
 
434
        self.build_tree_contents([('this-tree/foo/bar', 'qux')])
 
435
        this_tree.commit('changed qux')
 
436
        this_tree.merge_from_branch(other_tree.branch)
 
437
        self.assertEqual(1, len(this_tree.conflicts()))
 
438
        this_tree.revert(['foo'])
 
439
        self.assertEqual(0, len(this_tree.conflicts()))
 
440
 
 
441
 
 
442
class TestAutoResolve(TestCaseWithTransport):
 
443
 
 
444
    def test_auto_resolve(self):
 
445
        base = self.make_branch_and_tree('base')
 
446
        self.build_tree_contents([('base/hello', 'Hello')])
 
447
        base.add('hello', 'hello_id')
 
448
        base.commit('Hello')
 
449
        other = base.bzrdir.sprout('other').open_workingtree()
 
450
        self.build_tree_contents([('other/hello', 'hELLO')])
 
451
        other.commit('Case switch')
 
452
        this = base.bzrdir.sprout('this').open_workingtree()
 
453
        self.assertPathExists('this/hello')
 
454
        self.build_tree_contents([('this/hello', 'Hello World')])
 
455
        this.commit('Add World')
 
456
        this.merge_from_branch(other.branch)
 
457
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
458
                         this.conflicts())
 
459
        this.auto_resolve()
 
460
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
461
                         this.conflicts())
 
462
        self.build_tree_contents([('this/hello', '<<<<<<<')])
 
463
        this.auto_resolve()
 
464
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
465
                         this.conflicts())
 
466
        self.build_tree_contents([('this/hello', '=======')])
 
467
        this.auto_resolve()
 
468
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
469
                         this.conflicts())
 
470
        self.build_tree_contents([('this/hello', '\n>>>>>>>')])
 
471
        remaining, resolved = this.auto_resolve()
 
472
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
473
                         this.conflicts())
 
474
        self.assertEqual([], resolved)
 
475
        self.build_tree_contents([('this/hello', 'hELLO wORLD')])
 
476
        remaining, resolved = this.auto_resolve()
 
477
        self.assertEqual([], this.conflicts())
 
478
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
479
                         resolved)
 
480
        self.assertPathDoesNotExist('this/hello.BASE')
 
481
 
 
482
    def test_auto_resolve_dir(self):
 
483
        tree = self.make_branch_and_tree('tree')
 
484
        self.build_tree(['tree/hello/'])
 
485
        tree.add('hello', 'hello-id')
 
486
        file_conflict = conflicts.TextConflict('file', 'hello-id')
 
487
        tree.set_conflicts(conflicts.ConflictList([file_conflict]))
 
488
        tree.auto_resolve()
 
489
 
 
490
 
 
491
class TestFindTrees(TestCaseWithTransport):
 
492
 
 
493
    def test_find_trees(self):
 
494
        self.make_branch_and_tree('foo')
 
495
        self.make_branch_and_tree('foo/bar')
 
496
        # Sticking a tree inside a control dir is heinous, so let's skip it
 
497
        self.make_branch_and_tree('foo/.bzr/baz')
 
498
        self.make_branch('qux')
 
499
        trees = workingtree.WorkingTree.find_trees('.')
 
500
        self.assertEqual(2, len(list(trees)))