~ubuntu-branches/ubuntu/lucid/bzr/lucid-proposed

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Jeff Bailey
  • Date: 2006-03-20 08:31:00 UTC
  • mfrom: (1.1.2 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060320083100-ovdi2ssuw0epcx8s
Tags: 0.8~200603200831-0ubuntu1
* Snapshot uploaded to Dapper at Martin Pool's request.

* Disable testsuite for upload.  Fakeroot and the testsuite don't
  play along.

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 test_revert(self):
 
84
        """Test selected-file revert"""
 
85
        tree = self.make_branch_and_tree('.')
 
86
 
 
87
        self.build_tree(['hello.txt'])
 
88
        file('hello.txt', 'w').write('initial hello')
 
89
 
 
90
        self.assertRaises(NotVersionedError,
 
91
                          tree.revert, ['hello.txt'])
 
92
        tree.add(['hello.txt'])
 
93
        tree.commit('create initial hello.txt')
 
94
 
 
95
        self.check_file_contents('hello.txt', 'initial hello')
 
96
        file('hello.txt', 'w').write('new hello')
 
97
        self.check_file_contents('hello.txt', 'new hello')
 
98
 
 
99
        # revert file modified since last revision
 
100
        tree.revert(['hello.txt'])
 
101
        self.check_file_contents('hello.txt', 'initial hello')
 
102
        self.check_file_contents('hello.txt~', 'new hello')
 
103
 
 
104
        # reverting again does not clobber the backup
 
105
        tree.revert(['hello.txt'])
 
106
        self.check_file_contents('hello.txt', 'initial hello')
 
107
        self.check_file_contents('hello.txt~', 'new hello')
 
108
 
 
109
    def test_unknowns(self):
 
110
        tree = self.make_branch_and_tree('.')
 
111
        self.build_tree(['hello.txt',
 
112
                         'hello.txt~'])
 
113
        self.assertEquals(list(tree.unknowns()),
 
114
                          ['hello.txt'])
 
115
 
 
116
    def test_hashcache(self):
 
117
        from bzrlib.tests.test_hashcache import pause
 
118
        tree = self.make_branch_and_tree('.')
 
119
        self.build_tree(['hello.txt',
 
120
                         'hello.txt~'])
 
121
        tree.add('hello.txt')
 
122
        pause()
 
123
        sha = tree.get_file_sha1(tree.path2id('hello.txt'))
 
124
        self.assertEqual(1, tree._hashcache.miss_count)
 
125
        tree2 = WorkingTree.open('.')
 
126
        sha2 = tree2.get_file_sha1(tree2.path2id('hello.txt'))
 
127
        self.assertEqual(0, tree2._hashcache.miss_count)
 
128
        self.assertEqual(1, tree2._hashcache.hit_count)
 
129
 
 
130
    def test_initialize(self):
 
131
        # initialize should create a working tree and branch in an existing dir
 
132
        t = self.make_branch_and_tree('.')
 
133
        b = Branch.open('.')
 
134
        self.assertEqual(t.branch.base, b.base)
 
135
        t2 = WorkingTree.open('.')
 
136
        self.assertEqual(t.basedir, t2.basedir)
 
137
        self.assertEqual(b.base, t2.branch.base)
 
138
        # TODO maybe we should check the branch format? not sure if its
 
139
        # appropriate here.
 
140
 
 
141
    def test_rename_dirs(self):
 
142
        """Test renaming directories and the files within them."""
 
143
        wt = self.make_branch_and_tree('.')
 
144
        b = wt.branch
 
145
        self.build_tree(['dir/', 'dir/sub/', 'dir/sub/file'])
 
146
        wt.add(['dir', 'dir/sub', 'dir/sub/file'])
 
147
 
 
148
        wt.commit('create initial state')
 
149
 
 
150
        revid = b.revision_history()[0]
 
151
        self.log('first revision_id is {%s}' % revid)
 
152
        
 
153
        inv = b.repository.get_revision_inventory(revid)
 
154
        self.log('contents of inventory: %r' % inv.entries())
 
155
 
 
156
        self.check_inventory_shape(inv,
 
157
                                   ['dir', 'dir/sub', 'dir/sub/file'])
 
158
 
 
159
        wt.rename_one('dir', 'newdir')
 
160
 
 
161
        self.check_inventory_shape(wt.read_working_inventory(),
 
162
                                   ['newdir', 'newdir/sub', 'newdir/sub/file'])
 
163
 
 
164
        wt.rename_one('newdir/sub', 'newdir/newsub')
 
165
        self.check_inventory_shape(wt.read_working_inventory(),
 
166
                                   ['newdir', 'newdir/newsub',
 
167
                                    'newdir/newsub/file'])
 
168
 
 
169
    def test_add_in_unversioned(self):
 
170
        """Try to add a file in an unversioned directory.
 
171
 
 
172
        "bzr add" adds the parent as necessary, but simple working tree add
 
173
        doesn't do that.
 
174
        """
 
175
        from bzrlib.errors import NotVersionedError
 
176
        wt = self.make_branch_and_tree('.')
 
177
        self.build_tree(['foo/',
 
178
                         'foo/hello'])
 
179
        self.assertRaises(NotVersionedError,
 
180
                          wt.add,
 
181
                          'foo/hello')
 
182
 
 
183
    def test_add_missing(self):
 
184
        # adding a msising file -> NoSuchFile
 
185
        wt = self.make_branch_and_tree('.')
 
186
        self.assertRaises(errors.NoSuchFile, wt.add, 'fpp')
 
187
 
 
188
    def test_remove_verbose(self):
 
189
        #FIXME the remove api should not print or otherwise depend on the
 
190
        # text UI - RBC 20060124
 
191
        wt = self.make_branch_and_tree('.')
 
192
        self.build_tree(['hello'])
 
193
        wt.add(['hello'])
 
194
        wt.commit(message='add hello')
 
195
        stdout = StringIO()
 
196
        stderr = StringIO()
 
197
        self.assertEqual(None, self.apply_redirected(None, stdout, stderr,
 
198
                                                     wt.remove,
 
199
                                                     ['hello'],
 
200
                                                     verbose=True))
 
201
        self.assertEqual('?       hello\n', stdout.getvalue())
 
202
        self.assertEqual('', stderr.getvalue())
 
203
 
 
204
    def test_clone_trivial(self):
 
205
        wt = self.make_branch_and_tree('source')
 
206
        cloned_dir = wt.bzrdir.clone('target')
 
207
        cloned = cloned_dir.open_workingtree()
 
208
        self.assertEqual(cloned.last_revision(), wt.last_revision())
 
209
 
 
210
    def test_last_revision(self):
 
211
        wt = self.make_branch_and_tree('source')
 
212
        self.assertEqual(None, wt.last_revision())
 
213
        wt.commit('A', allow_pointless=True, rev_id='A')
 
214
        self.assertEqual('A', wt.last_revision())
 
215
 
 
216
    def test_set_last_revision(self):
 
217
        wt = self.make_branch_and_tree('source')
 
218
        self.assertEqual(None, wt.last_revision())
 
219
        # cannot set the last revision to one not in the branch history.
 
220
        self.assertRaises(errors.NoSuchRevision, wt.set_last_revision, 'A')
 
221
        wt.commit('A', allow_pointless=True, rev_id='A')
 
222
        self.assertEqual('A', wt.last_revision())
 
223
        # None is aways in the branch
 
224
        wt.set_last_revision(None)
 
225
        self.assertEqual(None, wt.last_revision())
 
226
        # and now we can set it to 'A'
 
227
        # because some formats mutate the branch to set it on the tree
 
228
        # we need to alter the branch to let this pass.
 
229
        wt.branch.set_revision_history(['A', 'B'])
 
230
        wt.set_last_revision('A')
 
231
        self.assertEqual('A', wt.last_revision())
 
232
 
 
233
    def test_set_last_revision_different_to_branch(self):
 
234
        # working tree formats from the meta-dir format and newer support
 
235
        # setting the last revision on a tree independently of that on the 
 
236
        # branch. Its concievable that some future formats may want to 
 
237
        # couple them again (i.e. because its really a smart server and
 
238
        # the working tree will always match the branch). So we test
 
239
        # that formats where initialising a branch does not initialise a 
 
240
        # tree - and thus have separable entities - support skewing the 
 
241
        # two things.
 
242
        branch = self.make_branch('tree')
 
243
        try:
 
244
            # if there is a working tree now, this is not supported.
 
245
            branch.bzrdir.open_workingtree()
 
246
            return
 
247
        except errors.NoWorkingTree:
 
248
            pass
 
249
        wt = branch.bzrdir.create_workingtree()
 
250
        wt.commit('A', allow_pointless=True, rev_id='A')
 
251
        wt.set_last_revision(None)
 
252
        self.assertEqual(None, wt.last_revision())
 
253
        self.assertEqual('A', wt.branch.last_revision())
 
254
        # and now we can set it back to 'A'
 
255
        wt.set_last_revision('A')
 
256
        self.assertEqual('A', wt.last_revision())
 
257
        self.assertEqual('A', wt.branch.last_revision())
 
258
 
 
259
    def test_clone_and_commit_preserves_last_revision(self):
 
260
        wt = self.make_branch_and_tree('source')
 
261
        cloned_dir = wt.bzrdir.clone('target')
 
262
        wt.commit('A', allow_pointless=True, rev_id='A')
 
263
        self.assertNotEqual(cloned_dir.open_workingtree().last_revision(),
 
264
                            wt.last_revision())
 
265
 
 
266
    def test_clone_preserves_content(self):
 
267
        wt = self.make_branch_and_tree('source')
 
268
        self.build_tree(['added', 'deleted', 'notadded'], transport=wt.bzrdir.transport.clone('..'))
 
269
        wt.add('deleted', 'deleted')
 
270
        wt.commit('add deleted')
 
271
        wt.remove('deleted')
 
272
        wt.add('added', 'added')
 
273
        cloned_dir = wt.bzrdir.clone('target')
 
274
        cloned = cloned_dir.open_workingtree()
 
275
        cloned_transport = cloned.bzrdir.transport.clone('..')
 
276
        self.assertFalse(cloned_transport.has('deleted'))
 
277
        self.assertTrue(cloned_transport.has('added'))
 
278
        self.assertFalse(cloned_transport.has('notadded'))
 
279
        self.assertEqual('added', cloned.path2id('added'))
 
280
        self.assertEqual(None, cloned.path2id('deleted'))
 
281
        self.assertEqual(None, cloned.path2id('notadded'))
 
282
        
 
283
    def test_basis_tree_returns_last_revision(self):
 
284
        wt = self.make_branch_and_tree('.')
 
285
        self.build_tree(['foo'])
 
286
        wt.add('foo', 'foo-id')
 
287
        wt.commit('A', rev_id='A')
 
288
        wt.rename_one('foo', 'bar')
 
289
        wt.commit('B', rev_id='B')
 
290
        wt.set_last_revision('B')
 
291
        tree = wt.basis_tree()
 
292
        self.failUnless(tree.has_filename('bar'))
 
293
        wt.set_last_revision('A')
 
294
        tree = wt.basis_tree()
 
295
        self.failUnless(tree.has_filename('foo'))
 
296
 
 
297
    def test_clone_tree_revision(self):
 
298
        # make a tree with a last-revision,
 
299
        # and clone it with a different last-revision, this should switch
 
300
        # do it.
 
301
        #
 
302
        # also test that the content is merged
 
303
        # and conflicts recorded.
 
304
        # This should merge between the trees - local edits should be preserved
 
305
        # but other changes occured.
 
306
        # we test this by having one file that does
 
307
        # not change between two revisions, and another that does -
 
308
        # if the changed one is not changed, fail,
 
309
        # if the one that did not change has lost a local change, fail.
 
310
        # 
 
311
        raise TestSkipped('revision limiting is not implemented yet.')
 
312
 
 
313
    def test_initialize_with_revision_id(self):
 
314
        # a bzrdir can construct a working tree for itself @ a specific revision.
 
315
        source = self.make_branch_and_tree('source')
 
316
        source.commit('a', rev_id='a', allow_pointless=True)
 
317
        source.commit('b', rev_id='b', allow_pointless=True)
 
318
        self.build_tree(['new/'])
 
319
        made_control = self.bzrdir_format.initialize('new')
 
320
        source.branch.repository.clone(made_control)
 
321
        source.branch.clone(made_control)
 
322
        made_tree = self.workingtree_format.initialize(made_control, revision_id='a')
 
323
        self.assertEqual('a', made_tree.last_revision())
 
324
 
 
325
    def test_commit_sets_last_revision(self):
 
326
        tree = self.make_branch_and_tree('tree')
 
327
        tree.commit('foo', rev_id='foo', allow_pointless=True)
 
328
        self.assertEqual('foo', tree.last_revision())
 
329
 
 
330
    def test_commit_local_unbound(self):
 
331
        # using the library api to do a local commit on unbound branches is 
 
332
        # also an error
 
333
        tree = self.make_branch_and_tree('tree')
 
334
        self.assertRaises(errors.LocalRequiresBoundBranch,
 
335
                          tree.commit,
 
336
                          'foo',
 
337
                          local=True)
 
338
 
 
339
    def test_local_commit_ignores_master(self):
 
340
        # a --local commit does not require access to the master branch
 
341
        # at all, or even for it to exist.
 
342
        # we test this by setting up a bound branch and then corrupting
 
343
        # the master.
 
344
        master = self.make_branch('master')
 
345
        tree = self.make_branch_and_tree('tree')
 
346
        try:
 
347
            tree.branch.bind(master)
 
348
        except errors.UpgradeRequired:
 
349
            # older format.
 
350
            return
 
351
        master.bzrdir.transport.put('branch-format', StringIO('garbage'))
 
352
        del master
 
353
        # check its corrupted.
 
354
        self.assertRaises(errors.UnknownFormatError,
 
355
                          bzrdir.BzrDir.open,
 
356
                          'master')
 
357
        tree.commit('foo', rev_id='foo', local=True)
 
358
 
 
359
    def test_local_commit_does_not_push_to_master(self):
 
360
        # a --local commit does not require access to the master branch
 
361
        # at all, or even for it to exist.
 
362
        # we test that even when its available it does not push to it.
 
363
        master = self.make_branch('master')
 
364
        tree = self.make_branch_and_tree('tree')
 
365
        try:
 
366
            tree.branch.bind(master)
 
367
        except errors.UpgradeRequired:
 
368
            # older format.
 
369
            return
 
370
        tree.commit('foo', rev_id='foo', local=True)
 
371
        self.failIf(master.repository.has_revision('foo'))
 
372
        self.assertEqual(None, master.last_revision())
 
373
        
 
374
    def test_update_sets_last_revision(self):
 
375
        # working tree formats from the meta-dir format and newer support
 
376
        # setting the last revision on a tree independently of that on the 
 
377
        # branch. Its concievable that some future formats may want to 
 
378
        # couple them again (i.e. because its really a smart server and
 
379
        # the working tree will always match the branch). So we test
 
380
        # that formats where initialising a branch does not initialise a 
 
381
        # tree - and thus have separable entities - support skewing the 
 
382
        # two things.
 
383
        main_branch = self.make_branch('tree')
 
384
        try:
 
385
            # if there is a working tree now, this is not supported.
 
386
            main_branch.bzrdir.open_workingtree()
 
387
            return
 
388
        except errors.NoWorkingTree:
 
389
            pass
 
390
        wt = main_branch.bzrdir.create_workingtree()
 
391
        # create an out of date working tree by making a checkout in this
 
392
        # current format
 
393
        self.build_tree(['checkout/', 'tree/file'])
 
394
        checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
 
395
        bzrlib.branch.BranchReferenceFormat().initialize(checkout, main_branch)
 
396
        old_tree = self.workingtree_format.initialize(checkout)
 
397
        # now commit to 'tree'
 
398
        wt.add('file')
 
399
        wt.commit('A', rev_id='A')
 
400
        # and update old_tree
 
401
        self.assertEqual(0, old_tree.update())
 
402
        self.failUnlessExists('checkout/file')
 
403
        self.assertEqual('A', old_tree.last_revision())
 
404
 
 
405
    def test_update_returns_conflict_count(self):
 
406
        # working tree formats from the meta-dir format and newer support
 
407
        # setting the last revision on a tree independently of that on the 
 
408
        # branch. Its concievable that some future formats may want to 
 
409
        # couple them again (i.e. because its really a smart server and
 
410
        # the working tree will always match the branch). So we test
 
411
        # that formats where initialising a branch does not initialise a 
 
412
        # tree - and thus have separable entities - support skewing the 
 
413
        # two things.
 
414
        main_branch = self.make_branch('tree')
 
415
        try:
 
416
            # if there is a working tree now, this is not supported.
 
417
            main_branch.bzrdir.open_workingtree()
 
418
            return
 
419
        except errors.NoWorkingTree:
 
420
            pass
 
421
        wt = main_branch.bzrdir.create_workingtree()
 
422
        # create an out of date working tree by making a checkout in this
 
423
        # current format
 
424
        self.build_tree(['checkout/', 'tree/file'])
 
425
        checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
 
426
        bzrlib.branch.BranchReferenceFormat().initialize(checkout, main_branch)
 
427
        old_tree = self.workingtree_format.initialize(checkout)
 
428
        # now commit to 'tree'
 
429
        wt.add('file')
 
430
        wt.commit('A', rev_id='A')
 
431
        # and add a file file to the checkout
 
432
        self.build_tree(['checkout/file'])
 
433
        old_tree.add('file')
 
434
        # and update old_tree
 
435
        self.assertEqual(1, old_tree.update())
 
436
        self.assertEqual('A', old_tree.last_revision())
 
437
 
 
438
    def test_update_updates_bound_branch_no_local_commits(self):
 
439
        # doing an update in a tree updates the branch its bound to too.
 
440
        master_tree = self.make_branch_and_tree('master')
 
441
        tree = self.make_branch_and_tree('tree')
 
442
        try:
 
443
            tree.branch.bind(master_tree.branch)
 
444
        except errors.UpgradeRequired:
 
445
            # legacy branches cannot bind
 
446
            return
 
447
        master_tree.commit('foo', rev_id='foo', allow_pointless=True)
 
448
        tree.update()
 
449
        self.assertEqual('foo', tree.last_revision())
 
450
        self.assertEqual('foo', tree.branch.last_revision())
 
451
 
 
452
    def test_update_turns_local_commit_into_merge(self):
 
453
        # doing an update with a few local commits and no master commits
 
454
        # makes pending-merges. 
 
455
        # this is done so that 'bzr update; bzr revert' will always produce
 
456
        # an exact copy of the 'logical branch' - the referenced branch for
 
457
        # a checkout, and the master for a bound branch.
 
458
        # its possible that we should instead have 'bzr update' when there
 
459
        # is nothing new on the master leave the current commits intact and
 
460
        # alter 'revert' to revert to the master always. But for now, its
 
461
        # good.
 
462
        master_tree = self.make_branch_and_tree('master')
 
463
        tree = self.make_branch_and_tree('tree')
 
464
        try:
 
465
            tree.branch.bind(master_tree.branch)
 
466
        except errors.UpgradeRequired:
 
467
            # legacy branches cannot bind
 
468
            return
 
469
        tree.commit('foo', rev_id='foo', allow_pointless=True, local=True)
 
470
        tree.commit('bar', rev_id='bar', allow_pointless=True, local=True)
 
471
        tree.update()
 
472
        self.assertEqual(None, tree.last_revision())
 
473
        self.assertEqual([], tree.branch.revision_history())
 
474
        self.assertEqual(['bar'], tree.pending_merges())
 
475