~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_workingtree.py

  • Committer: Jelmer Vernooij
  • Date: 2015-11-15 02:30:05 UTC
  • mto: This revision was merged to the branch mainline in revision 6609.
  • Revision ID: jelmer@jelmer.uk-20151115023005-fcfi763b5eu1ne2o
Fix auodoc_rstx when running with LANG=C.

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
    transport,
 
23
    workingtree,
 
24
    workingtree_3,
 
25
    workingtree_4,
 
26
    )
 
27
from bzrlib.lock import write_locked
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())
84
 
 
85
 
 
86
 
class SampleTreeFormat(workingtree.WorkingTreeFormat):
 
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_from_string(self):
 
83
        self.assertIsInstance(
 
84
            SampleTreeFormat.from_string("Sample tree format."),
 
85
            SampleTreeFormat)
 
86
        self.assertRaises(AssertionError,
 
87
            SampleTreeFormat.from_string, "Different format string.")
 
88
 
 
89
    def test_get_set_default_format_by_key(self):
 
90
        old_format = workingtree.format_registry.get_default()
 
91
        # default is 6
 
92
        format = SampleTreeFormat()
 
93
        workingtree.format_registry.register(format)
 
94
        self.addCleanup(workingtree.format_registry.remove, format)
 
95
        self.assertTrue(isinstance(old_format, workingtree_4.WorkingTreeFormat6))
 
96
        workingtree.format_registry.set_default_key(format.get_format_string())
 
97
        try:
 
98
            # the default branch format is used by the meta dir format
 
99
            # which is not the default bzrdir format at this point
 
100
            dir = bzrdir.BzrDirMetaFormat1().initialize('.')
 
101
            dir.create_repository()
 
102
            dir.create_branch()
 
103
            result = dir.create_workingtree()
 
104
            self.assertEqual(result, 'A tree')
 
105
        finally:
 
106
            workingtree.format_registry.set_default_key(
 
107
                old_format.get_format_string())
 
108
        self.assertEqual(old_format, workingtree.format_registry.get_default())
 
109
 
 
110
    def test_open(self):
 
111
        tree = self.make_branch_and_tree('.')
 
112
        open_direct = workingtree.WorkingTree.open('.')
 
113
        self.assertEqual(tree.basedir, open_direct.basedir)
 
114
        open_no_args = workingtree.WorkingTree.open()
 
115
        self.assertEqual(tree.basedir, open_no_args.basedir)
 
116
 
 
117
    def test_open_containing(self):
 
118
        tree = self.make_branch_and_tree('.')
 
119
        open_direct, relpath = workingtree.WorkingTree.open_containing('.')
 
120
        self.assertEqual(tree.basedir, open_direct.basedir)
 
121
        self.assertEqual('', relpath)
 
122
        open_no_args, relpath = workingtree.WorkingTree.open_containing()
 
123
        self.assertEqual(tree.basedir, open_no_args.basedir)
 
124
        self.assertEqual('', relpath)
 
125
        open_subdir, relpath = workingtree.WorkingTree.open_containing('subdir')
 
126
        self.assertEqual(tree.basedir, open_subdir.basedir)
 
127
        self.assertEqual('subdir', relpath)
 
128
 
 
129
 
 
130
class SampleTreeFormat(workingtree.WorkingTreeFormatMetaDir):
87
131
    """A sample format
88
132
 
89
 
    this format is initializable, unsupported to aid in testing the 
 
133
    this format is initializable, unsupported to aid in testing the
90
134
    open and open_downlevel routines.
91
135
    """
92
136
 
93
 
    def get_format_string(self):
 
137
    @classmethod
 
138
    def get_format_string(cls):
94
139
        """See WorkingTreeFormat.get_format_string()."""
95
140
        return "Sample tree format."
96
141
 
97
 
    def initialize(self, a_bzrdir, revision_id=None):
 
142
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
143
                   accelerator_tree=None, hardlink=False):
98
144
        """Sample branches cannot be created."""
99
145
        t = a_bzrdir.get_workingtree_transport(self)
100
146
        t.put_bytes('format', self.get_format_string())
107
153
        return "opened tree."
108
154
 
109
155
 
 
156
class SampleExtraTreeFormat(workingtree.WorkingTreeFormat):
 
157
    """A sample format that does not support use in a metadir.
 
158
 
 
159
    """
 
160
 
 
161
    def get_format_string(self):
 
162
        # Not usable in a metadir, so no format string
 
163
        return None
 
164
 
 
165
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
166
                   accelerator_tree=None, hardlink=False):
 
167
        raise NotImplementedError(self.initialize)
 
168
 
 
169
    def is_supported(self):
 
170
        return False
 
171
 
 
172
    def open(self, transport, _found=False):
 
173
        raise NotImplementedError(self.open)
 
174
 
 
175
 
110
176
class TestWorkingTreeFormat(TestCaseWithTransport):
111
177
    """Tests for the WorkingTreeFormat facility."""
112
178
 
 
179
    def test_find_format_string(self):
 
180
        # is the right format object found for a working tree?
 
181
        branch = self.make_branch('branch')
 
182
        self.assertRaises(errors.NoWorkingTree,
 
183
            workingtree.WorkingTreeFormatMetaDir.find_format_string, branch.bzrdir)
 
184
        transport = branch.bzrdir.get_workingtree_transport(None)
 
185
        transport.mkdir('.')
 
186
        transport.put_bytes("format", "some format name")
 
187
        # The format does not have to be known by Bazaar,
 
188
        # find_format_string just retrieves the name
 
189
        self.assertEquals("some format name",
 
190
            workingtree.WorkingTreeFormatMetaDir.find_format_string(branch.bzrdir))
 
191
 
113
192
    def test_find_format(self):
114
193
        # is the right format object found for a working tree?
115
194
        # create a branch with a few known format objects.
119
198
            dir.create_repository()
120
199
            dir.create_branch()
121
200
            format.initialize(dir)
122
 
            t = get_transport(url)
123
 
            found_format = workingtree.WorkingTreeFormat.find_format(dir)
124
 
            self.failUnless(isinstance(found_format, format.__class__))
125
 
        check_format(workingtree.WorkingTreeFormat3(), "bar")
126
 
        
 
201
            t = transport.get_transport(url)
 
202
            found_format = workingtree.WorkingTreeFormatMetaDir.find_format(dir)
 
203
            self.assertIsInstance(found_format, format.__class__)
 
204
        check_format(workingtree_3.WorkingTreeFormat3(), "bar")
 
205
 
127
206
    def test_find_format_no_tree(self):
128
207
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
129
208
        self.assertRaises(errors.NoWorkingTree,
130
 
                          workingtree.WorkingTreeFormat.find_format,
 
209
                          workingtree.WorkingTreeFormatMetaDir.find_format,
131
210
                          dir)
132
211
 
133
212
    def test_find_format_unknown_format(self):
136
215
        dir.create_branch()
137
216
        SampleTreeFormat().initialize(dir)
138
217
        self.assertRaises(errors.UnknownFormatError,
139
 
                          workingtree.WorkingTreeFormat.find_format,
 
218
                          workingtree.WorkingTreeFormatMetaDir.find_format,
140
219
                          dir)
141
220
 
 
221
    def test_find_format_with_features(self):
 
222
        tree = self.make_branch_and_tree('.', format='2a')
 
223
        tree.update_feature_flags({"name": "necessity"})
 
224
        found_format = workingtree.WorkingTreeFormatMetaDir.find_format(
 
225
            tree.bzrdir)
 
226
        self.assertIsInstance(found_format, workingtree.WorkingTreeFormat)
 
227
        self.assertEquals(found_format.features.get("name"), "necessity")
 
228
        self.assertRaises(errors.MissingFeature, found_format.check_support_status,
 
229
            True)
 
230
        self.addCleanup(workingtree.WorkingTreeFormatMetaDir.unregister_feature,
 
231
            "name")
 
232
        workingtree.WorkingTreeFormatMetaDir.register_feature("name")
 
233
        found_format.check_support_status(True)
 
234
 
 
235
 
 
236
class TestWorkingTreeIterEntriesByDir_wSubtrees(TestCaseWithTransport):
 
237
 
 
238
    def make_simple_tree(self):
 
239
        tree = self.make_branch_and_tree('tree', format='development-subtree')
 
240
        self.build_tree(['tree/a/', 'tree/a/b/', 'tree/a/b/c'])
 
241
        tree.set_root_id('root-id')
 
242
        tree.add(['a', 'a/b', 'a/b/c'], ['a-id', 'b-id', 'c-id'])
 
243
        tree.commit('initial')
 
244
        return tree
 
245
 
 
246
    def test_just_directory(self):
 
247
        tree = self.make_simple_tree()
 
248
        self.assertEqual([('directory', 'root-id'),
 
249
                          ('directory', 'a-id'),
 
250
                          ('directory', 'b-id'),
 
251
                          ('file', 'c-id')],
 
252
                         [(ie.kind, ie.file_id)
 
253
                          for path, ie in tree.iter_entries_by_dir()])
 
254
        subtree = self.make_branch_and_tree('tree/a/b')
 
255
        self.assertEqual([('tree-reference', 'b-id')],
 
256
                         [(ie.kind, ie.file_id)
 
257
                          for path, ie in tree.iter_entries_by_dir(['b-id'])])
 
258
 
 
259
    def test_direct_subtree(self):
 
260
        tree = self.make_simple_tree()
 
261
        subtree = self.make_branch_and_tree('tree/a/b')
 
262
        self.assertEqual([('directory', 'root-id'),
 
263
                          ('directory', 'a-id'),
 
264
                          ('tree-reference', 'b-id')],
 
265
                         [(ie.kind, ie.file_id)
 
266
                          for path, ie in tree.iter_entries_by_dir()])
 
267
 
 
268
    def test_indirect_subtree(self):
 
269
        tree = self.make_simple_tree()
 
270
        subtree = self.make_branch_and_tree('tree/a')
 
271
        self.assertEqual([('directory', 'root-id'),
 
272
                          ('tree-reference', 'a-id')],
 
273
                         [(ie.kind, ie.file_id)
 
274
                          for path, ie in tree.iter_entries_by_dir()])
 
275
 
 
276
 
 
277
class TestWorkingTreeFormatRegistry(TestCase):
 
278
 
 
279
    def setUp(self):
 
280
        super(TestWorkingTreeFormatRegistry, self).setUp()
 
281
        self.registry = workingtree.WorkingTreeFormatRegistry()
 
282
 
142
283
    def test_register_unregister_format(self):
143
284
        format = SampleTreeFormat()
144
 
        # make a control dir
145
 
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
146
 
        dir.create_repository()
147
 
        dir.create_branch()
148
 
        # make a branch
149
 
        format.initialize(dir)
150
 
        # register a format for it.
151
 
        workingtree.WorkingTreeFormat.register_format(format)
152
 
        # which branch.Open will refuse (not supported)
153
 
        self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
154
 
        # but open_downlevel will work
155
 
        self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
156
 
        # unregister the format
157
 
        workingtree.WorkingTreeFormat.unregister_format(format)
 
285
        self.registry.register(format)
 
286
        self.assertEquals(format, self.registry.get("Sample tree format."))
 
287
        self.registry.remove(format)
 
288
        self.assertRaises(KeyError, self.registry.get, "Sample tree format.")
 
289
 
 
290
    def test_get_all(self):
 
291
        format = SampleTreeFormat()
 
292
        self.assertEquals([], self.registry._get_all())
 
293
        self.registry.register(format)
 
294
        self.assertEquals([format], self.registry._get_all())
 
295
 
 
296
    def test_register_extra(self):
 
297
        format = SampleExtraTreeFormat()
 
298
        self.assertEquals([], self.registry._get_all())
 
299
        self.registry.register_extra(format)
 
300
        self.assertEquals([format], self.registry._get_all())
 
301
 
 
302
    def test_register_extra_lazy(self):
 
303
        self.assertEquals([], self.registry._get_all())
 
304
        self.registry.register_extra_lazy("bzrlib.tests.test_workingtree",
 
305
            "SampleExtraTreeFormat")
 
306
        formats = self.registry._get_all()
 
307
        self.assertEquals(1, len(formats))
 
308
        self.assertIsInstance(formats[0], SampleExtraTreeFormat)
158
309
 
159
310
 
160
311
class TestWorkingTreeFormat3(TestCaseWithTransport):
164
315
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
165
316
        control.create_repository()
166
317
        control.create_branch()
167
 
        tree = workingtree.WorkingTreeFormat3().initialize(control)
 
318
        tree = workingtree_3.WorkingTreeFormat3().initialize(control)
168
319
        # we want:
169
320
        # format 'Bazaar-NG Working Tree format 3'
170
321
        # inventory = blank inventory
174
325
        t = control.get_workingtree_transport(None)
175
326
        self.assertEqualDiff('Bazaar-NG Working Tree format 3',
176
327
                             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(),
 
328
        self.assertEqualDiff(t.get('inventory').read(),
184
329
                              '<inventory format="5">\n'
185
330
                              '</inventory>\n',
186
331
                             )
189
334
        self.assertFalse(t.has('inventory.basis'))
190
335
        # no last-revision file means 'None' or 'NULLREVISION'
191
336
        self.assertFalse(t.has('last-revision'))
192
 
        # TODO RBC 20060210 do a commit, check the inventory.basis is created 
 
337
        # TODO RBC 20060210 do a commit, check the inventory.basis is created
193
338
        # correctly and last-revision file becomes present.
194
339
 
195
340
    def test_uses_lockdir(self):
196
341
        """WorkingTreeFormat3 uses its own LockDir:
197
 
            
 
342
 
198
343
            - lock is a directory
199
344
            - when the WorkingTree is locked, LockDir can see that
200
345
        """
204
349
        repo = dir.create_repository()
205
350
        branch = dir.create_branch()
206
351
        try:
207
 
            tree = workingtree.WorkingTreeFormat3().initialize(dir)
 
352
            tree = workingtree_3.WorkingTreeFormat3().initialize(dir)
208
353
        except errors.NotLocalUrl:
209
354
            raise TestSkipped('Not a local URL')
210
355
        self.assertIsDirectory('.bzr', t)
221
366
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
222
367
        control.create_repository()
223
368
        control.create_branch()
224
 
        tree = workingtree.WorkingTreeFormat3().initialize(control)
225
 
        tree._control_files._transport.delete("pending-merges")
 
369
        tree = workingtree_3.WorkingTreeFormat3().initialize(control)
 
370
        tree._transport.delete("pending-merges")
226
371
        self.assertEqual([], tree.get_parent_ids())
227
372
 
228
373
 
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
374
class InstrumentedTree(object):
515
375
    """A instrumented tree to check the needs_tree_write_lock decorator."""
516
376
 
528
388
    @needs_tree_write_lock
529
389
    def method_that_raises(self):
530
390
        """This method causes an exception when called with parameters.
531
 
        
 
391
 
532
392
        This allows the decorator code to be checked - it should still call
533
393
        unlock.
534
394
        """
545
405
        self.assertEqual(
546
406
            'method_with_tree_write_lock',
547
407
            tree.method_with_tree_write_lock.__name__)
548
 
        self.assertEqual(
 
408
        self.assertDocstring(
549
409
            "A lock_tree_write decorated method that returns its arguments.",
550
 
            tree.method_with_tree_write_lock.__doc__)
 
410
            tree.method_with_tree_write_lock)
551
411
        args = (1, 2, 3)
552
412
        kwargs = {'a':'b'}
553
413
        result = tree.method_with_tree_write_lock(1,2,3, a='b')
555
415
        self.assertEqual(['t', 'u'], tree._locks)
556
416
        self.assertRaises(TypeError, tree.method_that_raises, 'foo')
557
417
        self.assertEqual(['t', 'u', 't', 'u'], tree._locks)
 
418
 
 
419
 
 
420
class TestRevert(TestCaseWithTransport):
 
421
 
 
422
    def test_revert_conflicts_recursive(self):
 
423
        this_tree = self.make_branch_and_tree('this-tree')
 
424
        self.build_tree_contents([('this-tree/foo/',),
 
425
                                  ('this-tree/foo/bar', 'bar')])
 
426
        this_tree.add(['foo', 'foo/bar'])
 
427
        this_tree.commit('created foo/bar')
 
428
        other_tree = this_tree.bzrdir.sprout('other-tree').open_workingtree()
 
429
        self.build_tree_contents([('other-tree/foo/bar', 'baz')])
 
430
        other_tree.commit('changed bar')
 
431
        self.build_tree_contents([('this-tree/foo/bar', 'qux')])
 
432
        this_tree.commit('changed qux')
 
433
        this_tree.merge_from_branch(other_tree.branch)
 
434
        self.assertEqual(1, len(this_tree.conflicts()))
 
435
        this_tree.revert(['foo'])
 
436
        self.assertEqual(0, len(this_tree.conflicts()))
 
437
 
 
438
 
 
439
class TestAutoResolve(TestCaseWithTransport):
 
440
 
 
441
    def test_auto_resolve(self):
 
442
        base = self.make_branch_and_tree('base')
 
443
        self.build_tree_contents([('base/hello', 'Hello')])
 
444
        base.add('hello', 'hello_id')
 
445
        base.commit('Hello')
 
446
        other = base.bzrdir.sprout('other').open_workingtree()
 
447
        self.build_tree_contents([('other/hello', 'hELLO')])
 
448
        other.commit('Case switch')
 
449
        this = base.bzrdir.sprout('this').open_workingtree()
 
450
        self.assertPathExists('this/hello')
 
451
        self.build_tree_contents([('this/hello', 'Hello World')])
 
452
        this.commit('Add World')
 
453
        this.merge_from_branch(other.branch)
 
454
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
455
                         this.conflicts())
 
456
        this.auto_resolve()
 
457
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
458
                         this.conflicts())
 
459
        self.build_tree_contents([('this/hello', '<<<<<<<')])
 
460
        this.auto_resolve()
 
461
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
462
                         this.conflicts())
 
463
        self.build_tree_contents([('this/hello', '=======')])
 
464
        this.auto_resolve()
 
465
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
466
                         this.conflicts())
 
467
        self.build_tree_contents([('this/hello', '\n>>>>>>>')])
 
468
        remaining, resolved = this.auto_resolve()
 
469
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
470
                         this.conflicts())
 
471
        self.assertEqual([], resolved)
 
472
        self.build_tree_contents([('this/hello', 'hELLO wORLD')])
 
473
        remaining, resolved = this.auto_resolve()
 
474
        self.assertEqual([], this.conflicts())
 
475
        self.assertEqual([conflicts.TextConflict('hello', 'hello_id')],
 
476
                         resolved)
 
477
        self.assertPathDoesNotExist('this/hello.BASE')
 
478
 
 
479
    def test_auto_resolve_dir(self):
 
480
        tree = self.make_branch_and_tree('tree')
 
481
        self.build_tree(['tree/hello/'])
 
482
        tree.add('hello', 'hello-id')
 
483
        file_conflict = conflicts.TextConflict('file', 'hello-id')
 
484
        tree.set_conflicts(conflicts.ConflictList([file_conflict]))
 
485
        tree.auto_resolve()
 
486
 
 
487
 
 
488
class TestFindTrees(TestCaseWithTransport):
 
489
 
 
490
    def test_find_trees(self):
 
491
        self.make_branch_and_tree('foo')
 
492
        self.make_branch_and_tree('foo/bar')
 
493
        # Sticking a tree inside a control dir is heinous, so let's skip it
 
494
        self.make_branch_and_tree('foo/.bzr/baz')
 
495
        self.make_branch('qux')
 
496
        trees = workingtree.WorkingTree.find_trees('.')
 
497
        self.assertEqual(2, len(list(trees)))
 
498
 
 
499
 
 
500
class TestStoredUncommitted(TestCaseWithTransport):
 
501
 
 
502
    def store_uncommitted(self):
 
503
        tree = self.make_branch_and_tree('tree')
 
504
        tree.commit('get root in there')
 
505
        self.build_tree_contents([('tree/file', 'content')])
 
506
        tree.add('file', 'file-id')
 
507
        tree.store_uncommitted()
 
508
        return tree
 
509
 
 
510
    def test_store_uncommitted(self):
 
511
        self.store_uncommitted()
 
512
        self.assertPathDoesNotExist('tree/file')
 
513
 
 
514
    def test_store_uncommitted_no_change(self):
 
515
        tree = self.make_branch_and_tree('tree')
 
516
        tree.commit('get root in there')
 
517
        tree.store_uncommitted()
 
518
        self.assertIs(None, tree.branch.get_unshelver(tree))
 
519
 
 
520
    def test_restore_uncommitted(self):
 
521
        with write_locked(self.store_uncommitted()) as tree:
 
522
            tree.restore_uncommitted()
 
523
            self.assertPathExists('tree/file')
 
524
            self.assertIs(None, tree.branch.get_unshelver(tree))
 
525
 
 
526
    def test_restore_uncommitted_none(self):
 
527
        tree = self.make_branch_and_tree('tree')
 
528
        tree.restore_uncommitted()