~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testworkingtree.py

  • Committer: Robert Collins
  • Date: 2005-10-18 13:11:57 UTC
  • mfrom: (1185.16.72) (0.2.1)
  • Revision ID: robertc@robertcollins.net-20051018131157-76a9970aa78e927e
Merged Martin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# (C) 2005 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
15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
17
 
18
 
from cStringIO import StringIO
19
18
import os
20
 
 
21
 
from bzrlib import ignores
22
 
import bzrlib
23
19
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
27
 
from bzrlib.lockdir import LockDir
28
 
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
 
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
 
20
from bzrlib.selftest import TestCaseInTempDir
32
21
from bzrlib.trace import mutter
33
 
from bzrlib.transport import get_transport
34
 
from bzrlib.workingtree import (
35
 
    TreeEntry,
36
 
    TreeDirectory,
37
 
    TreeFile,
38
 
    TreeLink,
39
 
    WorkingTree,
40
 
    )
 
22
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
 
23
                                WorkingTree)
41
24
 
42
 
class TestTreeDirectory(TestCaseWithTransport):
 
25
class TestTreeDirectory(TestCaseInTempDir):
43
26
 
44
27
    def test_kind_character(self):
45
28
        self.assertEqual(TreeDirectory().kind_character(), '/')
46
29
 
47
30
 
48
 
class TestTreeEntry(TestCaseWithTransport):
 
31
class TestTreeEntry(TestCaseInTempDir):
49
32
 
50
33
    def test_kind_character(self):
51
34
        self.assertEqual(TreeEntry().kind_character(), '???')
52
35
 
53
36
 
54
 
class TestTreeFile(TestCaseWithTransport):
 
37
class TestTreeFile(TestCaseInTempDir):
55
38
 
56
39
    def test_kind_character(self):
57
40
        self.assertEqual(TreeFile().kind_character(), '')
58
41
 
59
42
 
60
 
class TestTreeLink(TestCaseWithTransport):
 
43
class TestTreeLink(TestCaseInTempDir):
61
44
 
62
45
    def test_kind_character(self):
63
46
        self.assertEqual(TreeLink().kind_character(), '')
64
47
 
65
48
 
66
 
class TestDefaultFormat(TestCaseWithTransport):
67
 
 
68
 
    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):
87
 
    """A sample format
88
 
 
89
 
    this format is initializable, unsupported to aid in testing the 
90
 
    open and open_downlevel routines.
91
 
    """
92
 
 
93
 
    def get_format_string(self):
94
 
        """See WorkingTreeFormat.get_format_string()."""
95
 
        return "Sample tree format."
96
 
 
97
 
    def initialize(self, a_bzrdir, revision_id=None):
98
 
        """Sample branches cannot be created."""
99
 
        t = a_bzrdir.get_workingtree_transport(self)
100
 
        t.put_bytes('format', self.get_format_string())
101
 
        return 'A tree'
102
 
 
103
 
    def is_supported(self):
104
 
        return False
105
 
 
106
 
    def open(self, transport, _found=False):
107
 
        return "opened tree."
108
 
 
109
 
 
110
 
class TestWorkingTreeFormat(TestCaseWithTransport):
111
 
    """Tests for the WorkingTreeFormat facility."""
112
 
 
113
 
    def test_find_format(self):
114
 
        # is the right format object found for a working tree?
115
 
        # create a branch with a few known format objects.
116
 
        self.build_tree(["foo/", "bar/"])
117
 
        def check_format(format, url):
118
 
            dir = format._matchingbzrdir.initialize(url)
119
 
            dir.create_repository()
120
 
            dir.create_branch()
121
 
            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
 
        
127
 
    def test_find_format_no_tree(self):
128
 
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
129
 
        self.assertRaises(errors.NoWorkingTree,
130
 
                          workingtree.WorkingTreeFormat.find_format,
131
 
                          dir)
132
 
 
133
 
    def test_find_format_unknown_format(self):
134
 
        dir = bzrdir.BzrDirMetaFormat1().initialize('.')
135
 
        dir.create_repository()
136
 
        dir.create_branch()
137
 
        SampleTreeFormat().initialize(dir)
138
 
        self.assertRaises(errors.UnknownFormatError,
139
 
                          workingtree.WorkingTreeFormat.find_format,
140
 
                          dir)
141
 
 
142
 
    def test_register_unregister_format(self):
143
 
        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)
158
 
 
159
 
 
160
 
class TestWorkingTreeFormat3(TestCaseWithTransport):
161
 
    """Tests specific to WorkingTreeFormat3."""
162
 
 
163
 
    def test_disk_layout(self):
164
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
165
 
        control.create_repository()
166
 
        control.create_branch()
167
 
        tree = workingtree.WorkingTreeFormat3().initialize(control)
168
 
        # we want:
169
 
        # format 'Bazaar-NG Working Tree format 3'
170
 
        # inventory = blank inventory
171
 
        # pending-merges = ''
172
 
        # stat-cache = ??
173
 
        # no inventory.basis yet
174
 
        t = control.get_workingtree_transport(None)
175
 
        self.assertEqualDiff('Bazaar-NG Working Tree format 3',
176
 
                             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(),
184
 
                              '<inventory format="5">\n'
185
 
                              '</inventory>\n',
186
 
                             )
187
 
        self.assertEqualDiff('### bzr hashcache v5\n',
188
 
                             t.get('stat-cache').read())
189
 
        self.assertFalse(t.has('inventory.basis'))
190
 
        # no last-revision file means 'None' or 'NULLREVISION'
191
 
        self.assertFalse(t.has('last-revision'))
192
 
        # TODO RBC 20060210 do a commit, check the inventory.basis is created 
193
 
        # correctly and last-revision file becomes present.
194
 
 
195
 
    def test_uses_lockdir(self):
196
 
        """WorkingTreeFormat3 uses its own LockDir:
197
 
            
198
 
            - lock is a directory
199
 
            - when the WorkingTree is locked, LockDir can see that
200
 
        """
201
 
        t = self.get_transport()
202
 
        url = self.get_url()
203
 
        dir = bzrdir.BzrDirMetaFormat1().initialize(url)
204
 
        repo = dir.create_repository()
205
 
        branch = dir.create_branch()
206
 
        try:
207
 
            tree = workingtree.WorkingTreeFormat3().initialize(dir)
208
 
        except errors.NotLocalUrl:
209
 
            raise TestSkipped('Not a local URL')
210
 
        self.assertIsDirectory('.bzr', t)
211
 
        self.assertIsDirectory('.bzr/checkout', t)
212
 
        self.assertIsDirectory('.bzr/checkout/lock', t)
213
 
        our_lock = LockDir(t, '.bzr/checkout/lock')
214
 
        self.assertEquals(our_lock.peek(), None)
 
49
class TestWorkingTree(TestCaseInTempDir):
 
50
 
 
51
    def test_listfiles(self):
 
52
        branch = Branch.initialize('.')
 
53
        os.mkdir('dir')
 
54
        print >> open('file', 'w'), "content"
 
55
        os.symlink('target', 'symlink')
 
56
        tree = branch.working_tree()
 
57
        files = list(tree.list_files())
 
58
        self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
 
59
        self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
 
60
        self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
 
61
 
 
62
    def test_construct_with_branch(self):
 
63
        branch = Branch.initialize('.')
 
64
        tree = WorkingTree(branch.base, branch)
 
65
        self.assertEqual(branch, tree.branch)
 
66
        self.assertEqual(branch.inventory, tree._inventory)
 
67
        self.assertEqual(branch.base, tree.basedir)
 
68
    
 
69
    def test_construct_without_branch(self):
 
70
        branch = Branch.initialize('.')
 
71
        tree = WorkingTree(branch.base)
 
72
        self.assertEqual(branch.base, tree.branch.base)
 
73
        self.assertEqual(branch.inventory, tree._inventory)
 
74
        self.assertEqual(branch.base, tree.basedir)
 
75
 
 
76
    def test_basic_relpath(self):
 
77
        # for comprehensive relpath tests, see whitebox.py.
 
78
        branch = Branch.initialize('.')
 
79
        tree = WorkingTree(branch.base)
 
80
        self.assertEqual('child',
 
81
                         tree.relpath(os.path.join(os.getcwd(), 'child')))
 
82
 
 
83
    def test_lock_locks_branch(self):
 
84
        branch = Branch.initialize('.')
 
85
        tree = WorkingTree(branch.base)
 
86
        tree.lock_read()
 
87
        self.assertEqual(1, tree.branch._lock_count)
 
88
        self.assertEqual('r', tree.branch._lock_mode)
 
89
        tree.unlock()
 
90
        self.assertEqual(None, tree.branch._lock_count)
215
91
        tree.lock_write()
216
 
        self.assertTrue(our_lock.peek())
 
92
        self.assertEqual(1, tree.branch._lock_count)
 
93
        self.assertEqual('w', tree.branch._lock_mode)
217
94
        tree.unlock()
218
 
        self.assertEquals(our_lock.peek(), None)
219
 
 
220
 
    def test_missing_pending_merges(self):
221
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
222
 
        control.create_repository()
223
 
        control.create_branch()
224
 
        tree = workingtree.WorkingTreeFormat3().initialize(control)
225
 
        tree._control_files._transport.delete("pending-merges")
226
 
        self.assertEqual([], tree.get_parent_ids())
227
 
 
228
 
 
229
 
class TestFormat2WorkingTree(TestCaseWithTransport):
230
 
    """Tests that are specific to format 2 trees."""
231
 
 
232
 
    def create_format2_tree(self, url):
233
 
        return self.make_branch_and_tree(
234
 
            url, format=bzrlib.bzrdir.BzrDirFormat6())
235
 
 
236
 
    def test_conflicts(self):
237
 
        # test backwards compatability
238
 
        tree = self.create_format2_tree('.')
239
 
        self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
240
 
                          None)
241
 
        file('lala.BASE', 'wb').write('labase')
242
 
        expected = conflicts.ContentsConflict('lala')
243
 
        self.assertEqual(list(tree.conflicts()), [expected])
244
 
        file('lala', 'wb').write('la')
245
 
        tree.add('lala', 'lala-id')
246
 
        expected = conflicts.ContentsConflict('lala', file_id='lala-id')
247
 
        self.assertEqual(list(tree.conflicts()), [expected])
248
 
        file('lala.THIS', 'wb').write('lathis')
249
 
        file('lala.OTHER', 'wb').write('laother')
250
 
        # When "text conflict"s happen, stem, THIS and OTHER are text
251
 
        expected = conflicts.TextConflict('lala', file_id='lala-id')
252
 
        self.assertEqual(list(tree.conflicts()), [expected])
253
 
        os.unlink('lala.OTHER')
254
 
        os.mkdir('lala.OTHER')
255
 
        expected = conflicts.ContentsConflict('lala', file_id='lala-id')
256
 
        self.assertEqual(list(tree.conflicts()), [expected])
257
 
 
258
 
 
259
 
class TestNonFormatSpecificCode(TestCaseWithTransport):
260
 
    """This class contains tests of workingtree that are not format specific."""
261
 
 
262
 
    def test_gen_file_id(self):
263
 
        file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_file_id,
264
 
                                      'filename')
265
 
        self.assertStartsWith(file_id, 'filename-')
266
 
 
267
 
    def test_gen_root_id(self):
268
 
        file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_root_id)
269
 
        self.assertStartsWith(file_id, 'tree_root-')
270
 
        
271
 
 
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)
 
95
        self.assertEqual(None, tree.branch._lock_count)
 
96
 
 
97
    def get_pullable_branches(self):
 
98
        self.build_tree(['from/', 'from/file', 'to/'])
 
99
        br_a = Branch.initialize('from')
 
100
        br_a.add('file')
 
101
        br_a.commit('foo', rev_id='A')
 
102
        br_b = Branch.initialize('to')
 
103
        return br_a, br_b
 
104
 
 
105
    def test_pull(self):
 
106
        br_a, br_b = self.get_pullable_branches()
 
107
        br_b.working_tree().pull(br_a)
 
108
        self.failUnless(br_b.has_revision('A'))
 
109
        self.assertEqual(['A'], br_b.revision_history())
 
110
 
 
111
    def test_pull_clobbers(self):
 
112
        br_a, br_b = self.get_pullable_branches()
 
113
        br_b.commit('foo', rev_id='B')
 
114
        self.assertEqual(['B'], br_b.revision_history())
 
115
        br_b.working_tree().pull(br_a, clobber=True)
 
116
        self.failUnless(br_b.has_revision('A'))
 
117
        self.failUnless(br_b.has_revision('B'))
 
118
        self.assertEqual(['A'], br_b.revision_history())