~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/workingtree_implementations/test_workingtree.py

  • Committer: Martin Pool
  • Date: 2006-02-22 04:29:54 UTC
  • mfrom: (1566 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1569.
  • Revision ID: mbp@sourcefrog.net-20060222042954-60333f08dd56a646
[merge] from bzr.dev before integration
Fix undefined ordering in sign_my_revisions breaking tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# (C) 2005,2006 Canonical Ltd
 
2
# Authors:  Robert Collins <robert.collins@canonical.com>
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
from cStringIO import StringIO
 
19
import os
 
20
 
 
21
import bzrlib
 
22
import bzrlib.branch
 
23
from bzrlib.branch import Branch
 
24
import bzrlib.bzrdir as bzrdir
 
25
from bzrlib.bzrdir import BzrDir
 
26
import bzrlib.errors as errors
 
27
from bzrlib.errors import NotBranchError, NotVersionedError
 
28
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
 
29
from bzrlib.tests import TestSkipped
 
30
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
 
31
from bzrlib.trace import mutter
 
32
import bzrlib.workingtree as workingtree
 
33
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
 
34
                                WorkingTree)
 
35
 
 
36
 
 
37
class TestWorkingTree(TestCaseWithWorkingTree):
 
38
 
 
39
    def test_listfiles(self):
 
40
        tree = self.make_branch_and_tree('.')
 
41
        os.mkdir('dir')
 
42
        print >> open('file', 'w'), "content"
 
43
        if has_symlinks():
 
44
            os.symlink('target', 'symlink')
 
45
        files = list(tree.list_files())
 
46
        self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
 
47
        self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
 
48
        if has_symlinks():
 
49
            self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
 
50
 
 
51
    def test_open_containing(self):
 
52
        branch = self.make_branch_and_tree('.').branch
 
53
        wt, relpath = WorkingTree.open_containing()
 
54
        self.assertEqual('', relpath)
 
55
        self.assertEqual(wt.basedir + '/', branch.base)
 
56
        wt, relpath = WorkingTree.open_containing(u'.')
 
57
        self.assertEqual('', relpath)
 
58
        self.assertEqual(wt.basedir + '/', branch.base)
 
59
        wt, relpath = WorkingTree.open_containing('./foo')
 
60
        self.assertEqual('foo', relpath)
 
61
        self.assertEqual(wt.basedir + '/', branch.base)
 
62
        wt, relpath = WorkingTree.open_containing('file://' + getcwd() + '/foo')
 
63
        self.assertEqual('foo', relpath)
 
64
        self.assertEqual(wt.basedir + '/', branch.base)
 
65
 
 
66
    def test_basic_relpath(self):
 
67
        # for comprehensive relpath tests, see whitebox.py.
 
68
        tree = self.make_branch_and_tree('.')
 
69
        self.assertEqual('child',
 
70
                         tree.relpath(pathjoin(getcwd(), 'child')))
 
71
 
 
72
    def test_lock_locks_branch(self):
 
73
        tree = self.make_branch_and_tree('.')
 
74
        tree.lock_read()
 
75
        self.assertEqual('r', tree.branch.peek_lock_mode())
 
76
        tree.unlock()
 
77
        self.assertEqual(None, tree.branch.peek_lock_mode())
 
78
        tree.lock_write()
 
79
        self.assertEqual('w', tree.branch.peek_lock_mode())
 
80
        tree.unlock()
 
81
        self.assertEqual(None, tree.branch.peek_lock_mode())
 
82
 
 
83
    def get_pullable_trees(self):
 
84
        self.build_tree(['from/', 'from/file', 'to/'])
 
85
        tree = self.make_branch_and_tree('from')
 
86
        tree.add('file')
 
87
        tree.commit('foo', rev_id='A')
 
88
        tree_b = self.make_branch_and_tree('to')
 
89
        return tree, tree_b
 
90
 
 
91
    def test_pull(self):
 
92
        tree_a, tree_b = self.get_pullable_trees()
 
93
        tree_b.pull(tree_a.branch)
 
94
        self.failUnless(tree_b.branch.repository.has_revision('A'))
 
95
        self.assertEqual('A', tree_b.last_revision())
 
96
 
 
97
    def test_pull_overwrites(self):
 
98
        tree_a, tree_b = self.get_pullable_trees()
 
99
        tree_b.commit('foo', rev_id='B')
 
100
        self.assertEqual(['B'], tree_b.branch.revision_history())
 
101
        tree_b.pull(tree_a.branch, overwrite=True)
 
102
        self.failUnless(tree_b.branch.repository.has_revision('A'))
 
103
        self.failUnless(tree_b.branch.repository.has_revision('B'))
 
104
        self.assertEqual('A', tree_b.last_revision())
 
105
 
 
106
    def test_revert(self):
 
107
        """Test selected-file revert"""
 
108
        tree = self.make_branch_and_tree('.')
 
109
 
 
110
        self.build_tree(['hello.txt'])
 
111
        file('hello.txt', 'w').write('initial hello')
 
112
 
 
113
        self.assertRaises(NotVersionedError,
 
114
                          tree.revert, ['hello.txt'])
 
115
        tree.add(['hello.txt'])
 
116
        tree.commit('create initial hello.txt')
 
117
 
 
118
        self.check_file_contents('hello.txt', 'initial hello')
 
119
        file('hello.txt', 'w').write('new hello')
 
120
        self.check_file_contents('hello.txt', 'new hello')
 
121
 
 
122
        # revert file modified since last revision
 
123
        tree.revert(['hello.txt'])
 
124
        self.check_file_contents('hello.txt', 'initial hello')
 
125
        self.check_file_contents('hello.txt~', 'new hello')
 
126
 
 
127
        # reverting again does not clobber the backup
 
128
        tree.revert(['hello.txt'])
 
129
        self.check_file_contents('hello.txt', 'initial hello')
 
130
        self.check_file_contents('hello.txt~', 'new hello')
 
131
 
 
132
    def test_unknowns(self):
 
133
        tree = self.make_branch_and_tree('.')
 
134
        self.build_tree(['hello.txt',
 
135
                         'hello.txt~'])
 
136
        self.assertEquals(list(tree.unknowns()),
 
137
                          ['hello.txt'])
 
138
 
 
139
    def test_hashcache(self):
 
140
        from bzrlib.tests.test_hashcache import pause
 
141
        tree = self.make_branch_and_tree('.')
 
142
        self.build_tree(['hello.txt',
 
143
                         'hello.txt~'])
 
144
        tree.add('hello.txt')
 
145
        pause()
 
146
        sha = tree.get_file_sha1(tree.path2id('hello.txt'))
 
147
        self.assertEqual(1, tree._hashcache.miss_count)
 
148
        tree2 = WorkingTree.open('.')
 
149
        sha2 = tree2.get_file_sha1(tree2.path2id('hello.txt'))
 
150
        self.assertEqual(0, tree2._hashcache.miss_count)
 
151
        self.assertEqual(1, tree2._hashcache.hit_count)
 
152
 
 
153
    def test_initialize(self):
 
154
        # initialize should create a working tree and branch in an existing dir
 
155
        t = self.make_branch_and_tree('.')
 
156
        b = Branch.open('.')
 
157
        self.assertEqual(t.branch.base, b.base)
 
158
        t2 = WorkingTree.open('.')
 
159
        self.assertEqual(t.basedir, t2.basedir)
 
160
        self.assertEqual(b.base, t2.branch.base)
 
161
        # TODO maybe we should check the branch format? not sure if its
 
162
        # appropriate here.
 
163
 
 
164
    def test_rename_dirs(self):
 
165
        """Test renaming directories and the files within them."""
 
166
        wt = self.make_branch_and_tree('.')
 
167
        b = wt.branch
 
168
        self.build_tree(['dir/', 'dir/sub/', 'dir/sub/file'])
 
169
        wt.add(['dir', 'dir/sub', 'dir/sub/file'])
 
170
 
 
171
        wt.commit('create initial state')
 
172
 
 
173
        revid = b.revision_history()[0]
 
174
        self.log('first revision_id is {%s}' % revid)
 
175
        
 
176
        inv = b.repository.get_revision_inventory(revid)
 
177
        self.log('contents of inventory: %r' % inv.entries())
 
178
 
 
179
        self.check_inventory_shape(inv,
 
180
                                   ['dir', 'dir/sub', 'dir/sub/file'])
 
181
 
 
182
        wt.rename_one('dir', 'newdir')
 
183
 
 
184
        self.check_inventory_shape(wt.read_working_inventory(),
 
185
                                   ['newdir', 'newdir/sub', 'newdir/sub/file'])
 
186
 
 
187
        wt.rename_one('newdir/sub', 'newdir/newsub')
 
188
        self.check_inventory_shape(wt.read_working_inventory(),
 
189
                                   ['newdir', 'newdir/newsub',
 
190
                                    'newdir/newsub/file'])
 
191
 
 
192
    def test_add_in_unversioned(self):
 
193
        """Try to add a file in an unversioned directory.
 
194
 
 
195
        "bzr add" adds the parent as necessary, but simple working tree add
 
196
        doesn't do that.
 
197
        """
 
198
        from bzrlib.errors import NotVersionedError
 
199
        wt = self.make_branch_and_tree('.')
 
200
        self.build_tree(['foo/',
 
201
                         'foo/hello'])
 
202
        self.assertRaises(NotVersionedError,
 
203
                          wt.add,
 
204
                          'foo/hello')
 
205
 
 
206
    def test_add_missing(self):
 
207
        # adding a msising file -> NoSuchFile
 
208
        wt = self.make_branch_and_tree('.')
 
209
        self.assertRaises(errors.NoSuchFile, wt.add, 'fpp')
 
210
 
 
211
    def test_remove_verbose(self):
 
212
        #FIXME the remove api should not print or otherwise depend on the
 
213
        # text UI - RBC 20060124
 
214
        wt = self.make_branch_and_tree('.')
 
215
        self.build_tree(['hello'])
 
216
        wt.add(['hello'])
 
217
        wt.commit(message='add hello')
 
218
        stdout = StringIO()
 
219
        stderr = StringIO()
 
220
        self.assertEqual(None, self.apply_redirected(None, stdout, stderr,
 
221
                                                     wt.remove,
 
222
                                                     ['hello'],
 
223
                                                     verbose=True))
 
224
        self.assertEqual('?       hello\n', stdout.getvalue())
 
225
        self.assertEqual('', stderr.getvalue())
 
226
 
 
227
    def test_clone_trivial(self):
 
228
        wt = self.make_branch_and_tree('source')
 
229
        cloned_dir = wt.bzrdir.clone('target')
 
230
        cloned = cloned_dir.open_workingtree()
 
231
        self.assertEqual(cloned.last_revision(), wt.last_revision())
 
232
 
 
233
    def test_last_revision(self):
 
234
        wt = self.make_branch_and_tree('source')
 
235
        self.assertEqual(None, wt.last_revision())
 
236
        wt.commit('A', allow_pointless=True, rev_id='A')
 
237
        self.assertEqual('A', wt.last_revision())
 
238
 
 
239
    def test_set_last_revision(self):
 
240
        wt = self.make_branch_and_tree('source')
 
241
        self.assertEqual(None, wt.last_revision())
 
242
        # cannot set the last revision to one not in the branch history.
 
243
        self.assertRaises(errors.NoSuchRevision, wt.set_last_revision, 'A')
 
244
        wt.commit('A', allow_pointless=True, rev_id='A')
 
245
        self.assertEqual('A', wt.last_revision())
 
246
        # None is aways in the branch
 
247
        wt.set_last_revision(None)
 
248
        self.assertEqual(None, wt.last_revision())
 
249
        # and now we can set it to 'A'
 
250
        # because some formats mutate the branch to set it on the tree
 
251
        # we need to alter the branch to let this pass.
 
252
        wt.branch.set_revision_history(['A', 'B'])
 
253
        wt.set_last_revision('A')
 
254
        self.assertEqual('A', wt.last_revision())
 
255
 
 
256
    def test_set_last_revision_different_to_branch(self):
 
257
        # working tree formats from the meta-dir format and newer support
 
258
        # setting the last revision on a tree independently of that on the 
 
259
        # branch. Its concievable that some future formats may want to 
 
260
        # couple them again (i.e. because its really a smart server and
 
261
        # the working tree will always match the branch). So we test
 
262
        # that formats where initialising a branch does not initialise a 
 
263
        # tree - and thus have separable entities - support skewing the 
 
264
        # two things.
 
265
        branch = self.make_branch('tree')
 
266
        try:
 
267
            # if there is a working tree now, this is not supported.
 
268
            branch.bzrdir.open_workingtree()
 
269
            return
 
270
        except errors.NoWorkingTree:
 
271
            pass
 
272
        wt = branch.bzrdir.create_workingtree()
 
273
        wt.commit('A', allow_pointless=True, rev_id='A')
 
274
        wt.set_last_revision(None)
 
275
        self.assertEqual(None, wt.last_revision())
 
276
        self.assertEqual('A', wt.branch.last_revision())
 
277
        # and now we can set it back to 'A'
 
278
        wt.set_last_revision('A')
 
279
        self.assertEqual('A', wt.last_revision())
 
280
        self.assertEqual('A', wt.branch.last_revision())
 
281
 
 
282
    def test_clone_and_commit_preserves_last_revision(self):
 
283
        wt = self.make_branch_and_tree('source')
 
284
        cloned_dir = wt.bzrdir.clone('target')
 
285
        wt.commit('A', allow_pointless=True, rev_id='A')
 
286
        self.assertNotEqual(cloned_dir.open_workingtree().last_revision(),
 
287
                            wt.last_revision())
 
288
 
 
289
    def test_clone_preserves_content(self):
 
290
        wt = self.make_branch_and_tree('source')
 
291
        self.build_tree(['added', 'deleted', 'notadded'], transport=wt.bzrdir.transport.clone('..'))
 
292
        wt.add('deleted', 'deleted')
 
293
        wt.commit('add deleted')
 
294
        wt.remove('deleted')
 
295
        wt.add('added', 'added')
 
296
        cloned_dir = wt.bzrdir.clone('target')
 
297
        cloned = cloned_dir.open_workingtree()
 
298
        cloned_transport = cloned.bzrdir.transport.clone('..')
 
299
        self.assertFalse(cloned_transport.has('deleted'))
 
300
        self.assertTrue(cloned_transport.has('added'))
 
301
        self.assertFalse(cloned_transport.has('notadded'))
 
302
        self.assertEqual('added', cloned.path2id('added'))
 
303
        self.assertEqual(None, cloned.path2id('deleted'))
 
304
        self.assertEqual(None, cloned.path2id('notadded'))
 
305
        
 
306
    def test_basis_tree_returns_last_revision(self):
 
307
        wt = self.make_branch_and_tree('.')
 
308
        self.build_tree(['foo'])
 
309
        wt.add('foo', 'foo-id')
 
310
        wt.commit('A', rev_id='A')
 
311
        wt.rename_one('foo', 'bar')
 
312
        wt.commit('B', rev_id='B')
 
313
        wt.set_last_revision('B')
 
314
        tree = wt.basis_tree()
 
315
        self.failUnless(tree.has_filename('bar'))
 
316
        wt.set_last_revision('A')
 
317
        tree = wt.basis_tree()
 
318
        self.failUnless(tree.has_filename('foo'))
 
319
 
 
320
    def test_clone_tree_revision(self):
 
321
        # make a tree with a last-revision,
 
322
        # and clone it with a different last-revision, this should switch
 
323
        # do it.
 
324
        #
 
325
        # also test that the content is merged
 
326
        # and conflicts recorded.
 
327
        # This should merge between the trees - local edits should be preserved
 
328
        # but other changes occured.
 
329
        # we test this by having one file that does
 
330
        # not change between two revisions, and another that does -
 
331
        # if the changed one is not changed, fail,
 
332
        # if the one that did not change has lost a local change, fail.
 
333
        # 
 
334
        raise TestSkipped('revision limiting is not implemented yet.')
 
335
 
 
336
    def test_initialize_with_revision_id(self):
 
337
        # a bzrdir can construct a working tree for itself @ a specific revision.
 
338
        source = self.make_branch_and_tree('source')
 
339
        source.commit('a', rev_id='a', allow_pointless=True)
 
340
        source.commit('b', rev_id='b', allow_pointless=True)
 
341
        self.build_tree(['new/'])
 
342
        made_control = self.bzrdir_format.initialize('new')
 
343
        source.branch.repository.clone(made_control)
 
344
        source.branch.clone(made_control)
 
345
        made_tree = self.workingtree_format.initialize(made_control, revision_id='a')
 
346
        self.assertEqual('a', made_tree.last_revision())
 
347
 
 
348
    def test_commit_sets_last_revision(self):
 
349
        tree = self.make_branch_and_tree('tree')
 
350
        tree.commit('foo', rev_id='foo', allow_pointless=True)
 
351
        self.assertEqual('foo', tree.last_revision())
 
352
 
 
353
    def test_update_sets_last_revision(self):
 
354
        # working tree formats from the meta-dir format and newer support
 
355
        # setting the last revision on a tree independently of that on the 
 
356
        # branch. Its concievable that some future formats may want to 
 
357
        # couple them again (i.e. because its really a smart server and
 
358
        # the working tree will always match the branch). So we test
 
359
        # that formats where initialising a branch does not initialise a 
 
360
        # tree - and thus have separable entities - support skewing the 
 
361
        # two things.
 
362
        main_branch = self.make_branch('tree')
 
363
        try:
 
364
            # if there is a working tree now, this is not supported.
 
365
            main_branch.bzrdir.open_workingtree()
 
366
            return
 
367
        except errors.NoWorkingTree:
 
368
            pass
 
369
        wt = main_branch.bzrdir.create_workingtree()
 
370
        # create an out of date working tree by making a checkout in this
 
371
        # current format
 
372
        self.build_tree(['checkout/', 'tree/file'])
 
373
        checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
 
374
        bzrlib.branch.BranchReferenceFormat().initialize(checkout, main_branch)
 
375
        old_tree = self.workingtree_format.initialize(checkout)
 
376
        # now commit to 'tree'
 
377
        wt.add('file')
 
378
        wt.commit('A', rev_id='A')
 
379
        # and update old_tree
 
380
        self.assertEqual(0, old_tree.update())
 
381
        self.failUnlessExists('checkout/file')
 
382
        self.assertEqual('A', old_tree.last_revision())
 
383
 
 
384
    def test_update_returns_conflict_count(self):
 
385
        # working tree formats from the meta-dir format and newer support
 
386
        # setting the last revision on a tree independently of that on the 
 
387
        # branch. Its concievable that some future formats may want to 
 
388
        # couple them again (i.e. because its really a smart server and
 
389
        # the working tree will always match the branch). So we test
 
390
        # that formats where initialising a branch does not initialise a 
 
391
        # tree - and thus have separable entities - support skewing the 
 
392
        # two things.
 
393
        main_branch = self.make_branch('tree')
 
394
        try:
 
395
            # if there is a working tree now, this is not supported.
 
396
            main_branch.bzrdir.open_workingtree()
 
397
            return
 
398
        except errors.NoWorkingTree:
 
399
            pass
 
400
        wt = main_branch.bzrdir.create_workingtree()
 
401
        # create an out of date working tree by making a checkout in this
 
402
        # current format
 
403
        self.build_tree(['checkout/', 'tree/file'])
 
404
        checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
 
405
        bzrlib.branch.BranchReferenceFormat().initialize(checkout, main_branch)
 
406
        old_tree = self.workingtree_format.initialize(checkout)
 
407
        # now commit to 'tree'
 
408
        wt.add('file')
 
409
        wt.commit('A', rev_id='A')
 
410
        # and add a file file to the checkout
 
411
        self.build_tree(['checkout/file'])
 
412
        old_tree.add('file')
 
413
        # and update old_tree
 
414
        self.assertEqual(1, old_tree.update())
 
415
        self.assertEqual('A', old_tree.last_revision())
 
416