~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_workingtree.py

  • Committer: Robert Collins
  • Date: 2005-11-28 05:13:41 UTC
  • mfrom: (1185.33.54 merge-recovered)
  • Revision ID: robertc@robertcollins.net-20051128051341-059936f2f29a12c8
Merge from Martin. Adjust check to work with HTTP again.

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
20
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
 
21
from bzrlib.tests import TestCaseInTempDir
32
22
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
 
    )
 
23
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
 
24
                                WorkingTree)
41
25
 
42
 
class TestTreeDirectory(TestCaseWithTransport):
 
26
class TestTreeDirectory(TestCaseInTempDir):
43
27
 
44
28
    def test_kind_character(self):
45
29
        self.assertEqual(TreeDirectory().kind_character(), '/')
46
30
 
47
31
 
48
 
class TestTreeEntry(TestCaseWithTransport):
 
32
class TestTreeEntry(TestCaseInTempDir):
49
33
 
50
34
    def test_kind_character(self):
51
35
        self.assertEqual(TreeEntry().kind_character(), '???')
52
36
 
53
37
 
54
 
class TestTreeFile(TestCaseWithTransport):
 
38
class TestTreeFile(TestCaseInTempDir):
55
39
 
56
40
    def test_kind_character(self):
57
41
        self.assertEqual(TreeFile().kind_character(), '')
58
42
 
59
43
 
60
 
class TestTreeLink(TestCaseWithTransport):
 
44
class TestTreeLink(TestCaseInTempDir):
61
45
 
62
46
    def test_kind_character(self):
63
47
        self.assertEqual(TreeLink().kind_character(), '')
64
48
 
65
49
 
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)
 
50
class TestWorkingTree(TestCaseInTempDir):
 
51
 
 
52
    def test_listfiles(self):
 
53
        branch = Branch.initialize('.')
 
54
        os.mkdir('dir')
 
55
        print >> open('file', 'w'), "content"
 
56
        os.symlink('target', 'symlink')
 
57
        tree = branch.working_tree()
 
58
        files = list(tree.list_files())
 
59
        self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
 
60
        self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
 
61
        self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
 
62
 
 
63
    def test_open_containing(self):
 
64
        branch = Branch.initialize('.')
 
65
        wt, relpath = WorkingTree.open_containing()
 
66
        self.assertEqual('', relpath)
 
67
        self.assertEqual(wt.basedir, branch.base)
 
68
        wt, relpath = WorkingTree.open_containing('.')
 
69
        self.assertEqual('', relpath)
 
70
        self.assertEqual(wt.basedir, branch.base)
 
71
        wt, relpath = WorkingTree.open_containing('./foo')
 
72
        self.assertEqual('foo', relpath)
 
73
        self.assertEqual(wt.basedir, branch.base)
 
74
        # paths that are urls are just plain wrong for working trees.
 
75
        self.assertRaises(NotBranchError,
 
76
                          WorkingTree.open_containing, 
 
77
                          'file:///' + os.getcwdu())
 
78
 
 
79
    def test_construct_with_branch(self):
 
80
        branch = Branch.initialize('.')
 
81
        tree = WorkingTree(branch.base, branch)
 
82
        self.assertEqual(branch, tree.branch)
 
83
        self.assertEqual(branch.base, tree.basedir)
 
84
    
 
85
    def test_construct_without_branch(self):
 
86
        branch = Branch.initialize('.')
 
87
        tree = WorkingTree(branch.base)
 
88
        self.assertEqual(branch.base, tree.branch.base)
 
89
        self.assertEqual(branch.base, tree.basedir)
 
90
 
 
91
    def test_basic_relpath(self):
 
92
        # for comprehensive relpath tests, see whitebox.py.
 
93
        branch = Branch.initialize('.')
 
94
        tree = WorkingTree(branch.base)
 
95
        self.assertEqual('child',
 
96
                         tree.relpath(os.path.join(os.getcwd(), 'child')))
 
97
 
 
98
    def test_lock_locks_branch(self):
 
99
        branch = Branch.initialize('.')
 
100
        tree = WorkingTree(branch.base)
 
101
        tree.lock_read()
 
102
        self.assertEqual(1, tree.branch._lock_count)
 
103
        self.assertEqual('r', tree.branch._lock_mode)
 
104
        tree.unlock()
 
105
        self.assertEqual(None, tree.branch._lock_count)
215
106
        tree.lock_write()
216
 
        self.assertTrue(our_lock.peek())
 
107
        self.assertEqual(1, tree.branch._lock_count)
 
108
        self.assertEqual('w', tree.branch._lock_mode)
217
109
        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)
 
110
        self.assertEqual(None, tree.branch._lock_count)
 
111
 
 
112
    def get_pullable_branches(self):
 
113
        self.build_tree(['from/', 'from/file', 'to/'])
 
114
        br_a = Branch.initialize('from')
 
115
        tree = br_a.working_tree()
 
116
        tree.add('file')
 
117
        tree.commit('foo', rev_id='A')
 
118
        br_b = Branch.initialize('to')
 
119
        return br_a, br_b
 
120
 
 
121
    def test_pull(self):
 
122
        br_a, br_b = self.get_pullable_branches()
 
123
        br_b.working_tree().pull(br_a)
 
124
        self.failUnless(br_b.has_revision('A'))
 
125
        self.assertEqual(['A'], br_b.revision_history())
 
126
 
 
127
    def test_pull_overwrites(self):
 
128
        br_a, br_b = self.get_pullable_branches()
 
129
        br_b.working_tree().commit('foo', rev_id='B')
 
130
        self.assertEqual(['B'], br_b.revision_history())
 
131
        br_b.working_tree().pull(br_a, overwrite=True)
 
132
        self.failUnless(br_b.has_revision('A'))
 
133
        self.failUnless(br_b.has_revision('B'))
 
134
        self.assertEqual(['A'], br_b.revision_history())
 
135
 
 
136
    def test_revert(self):
 
137
        """Test selected-file revert"""
 
138
        b = Branch.initialize('.')
 
139
 
 
140
        self.build_tree(['hello.txt'])
 
141
        file('hello.txt', 'w').write('initial hello')
 
142
 
 
143
        self.assertRaises(NotVersionedError,
 
144
                          b.working_tree().revert, ['hello.txt'])
 
145
        tree = WorkingTree(b.base, b)
 
146
        tree.add(['hello.txt'])
 
147
        tree.commit('create initial hello.txt')
 
148
 
 
149
        self.check_file_contents('hello.txt', 'initial hello')
 
150
        file('hello.txt', 'w').write('new hello')
 
151
        self.check_file_contents('hello.txt', 'new hello')
 
152
 
 
153
        # revert file modified since last revision
 
154
        tree.revert(['hello.txt'])
 
155
        self.check_file_contents('hello.txt', 'initial hello')
 
156
        self.check_file_contents('hello.txt~', 'new hello')
 
157
 
 
158
        # reverting again does not clobber the backup
 
159
        tree.revert(['hello.txt'])
 
160
        self.check_file_contents('hello.txt', 'initial hello')
 
161
        self.check_file_contents('hello.txt~', 'new hello')
 
162
 
 
163
    def test_unknowns(self):
 
164
        b = Branch.initialize('.')
 
165
        tree = WorkingTree('.', b)
 
166
        self.build_tree(['hello.txt',
 
167
                         'hello.txt~'])
 
168
        self.assertEquals(list(tree.unknowns()),
 
169
                          ['hello.txt'])
 
170