~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_workingtree.py

mergeĀ fromĀ dirstate

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
from cStringIO import StringIO
19
19
import os
20
20
 
21
 
from bzrlib import dirstate, ignores
22
 
import bzrlib
 
21
from bzrlib import (
 
22
    bzrdir,
 
23
    conflicts,
 
24
    errors,
 
25
    workingtree,
 
26
    )
23
27
from bzrlib.branch import Branch
24
 
from bzrlib import bzrdir, conflicts, errors, workingtree
25
28
from bzrlib.bzrdir import BzrDir
26
 
from bzrlib.errors import NotBranchError, NotVersionedError
27
29
from bzrlib.lockdir import LockDir
28
30
from bzrlib.mutabletree import needs_tree_write_lock
29
 
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
31
from bzrlib.symbol_versioning import zero_thirteen
31
32
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
32
 
from bzrlib.trace import mutter
33
33
from bzrlib.transport import get_transport
34
34
from bzrlib.workingtree import (
35
35
    TreeEntry,
36
36
    TreeDirectory,
37
37
    TreeFile,
38
38
    TreeLink,
39
 
    WorkingTree,
40
39
    )
41
40
 
 
41
 
42
42
class TestTreeDirectory(TestCaseWithTransport):
43
43
 
44
44
    def test_kind_character(self):
220
220
        self.assertEqual([], tree.get_parent_ids())
221
221
 
222
222
 
223
 
class TestWorkingTreeFormat4(TestCaseWithTransport):
224
 
    """Tests specific to WorkingTreeFormat4."""
225
 
 
226
 
    def test_disk_layout(self):
227
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
228
 
        control.create_repository()
229
 
        control.create_branch()
230
 
        tree = workingtree.WorkingTreeFormat4().initialize(control)
231
 
        # we want:
232
 
        # format 'Bazaar Working Tree format 4'
233
 
        # stat-cache = ??
234
 
        t = control.get_workingtree_transport(None)
235
 
        self.assertEqualDiff('Bazaar Working Tree format 4\n',
236
 
                             t.get('format').read())
237
 
        self.assertEqualDiff('### bzr hashcache v5\n',
238
 
                             t.get('stat-cache').read())
239
 
        self.assertFalse(t.has('inventory.basis'))
240
 
        # no last-revision file means 'None' or 'NULLREVISION'
241
 
        self.assertFalse(t.has('last-revision'))
242
 
        # TODO RBC 20060210 do a commit, check the inventory.basis is created 
243
 
        # correctly and last-revision file becomes present.
244
 
        # manually make a dirstate toc check the format is as desired.
245
 
        state = dirstate.DirState.on_file(t.local_abspath('dirstate'))
246
 
        self.assertEqual([], state.get_parent_ids())
247
 
 
248
 
    def test_uses_lockdir(self):
249
 
        """WorkingTreeFormat4 uses its own LockDir:
250
 
            
251
 
            - lock is a directory
252
 
            - when the WorkingTree is locked, LockDir can see that
253
 
        """
254
 
        # this test could be factored into a subclass of tests common to both
255
 
        # format 3 and 4, but for now its not much of an issue as there is only one in common.
256
 
        t = self.get_transport()
257
 
        tree = self.make_workingtree()
258
 
        self.assertIsDirectory('.bzr', t)
259
 
        self.assertIsDirectory('.bzr/checkout', t)
260
 
        self.assertIsDirectory('.bzr/checkout/lock', t)
261
 
        our_lock = LockDir(t, '.bzr/checkout/lock')
262
 
        self.assertEquals(our_lock.peek(), None)
263
 
        tree.lock_write()
264
 
        self.assertTrue(our_lock.peek())
265
 
        tree.unlock()
266
 
        self.assertEquals(our_lock.peek(), None)
267
 
 
268
 
    def make_workingtree(self):
269
 
        url = self.get_url()
270
 
        dir = bzrdir.BzrDirMetaFormat1().initialize(url)
271
 
        repo = dir.create_repository()
272
 
        branch = dir.create_branch()
273
 
        try:
274
 
            return workingtree.WorkingTreeFormat4().initialize(dir)
275
 
        except errors.NotLocalUrl:
276
 
            raise TestSkipped('Not a local URL')
277
 
 
278
 
    # TODO: test that dirstate also stores & retrieves the parent list of 
279
 
    # workingtree-parent revisions, including when they have multiple parents.
280
 
    # (in other words, the case when we're constructing a merge of 
281
 
    # revisions which are themselves merges.)
282
 
 
283
 
    # The simplest case is that the the workingtree's primary 
284
 
    # parent tree can be retrieved.  This is required for all WorkingTrees, 
285
 
    # and covered by the generic tests.
286
 
 
287
 
    def test_dirstate_stores_all_parent_inventories(self):
288
 
        tree = self.make_workingtree()
289
 
 
290
 
        # We're going to build in tree a working tree 
291
 
        # with three parent trees, with some files in common.  
292
 
    
293
 
        # We really don't want to do commit or merge in the new dirstate-based
294
 
        # tree, because that might not work yet.  So instead we build
295
 
        # revisions elsewhere and pull them across, doing by hand part of the
296
 
        # work that merge would do.
297
 
 
298
 
        subtree = self.make_branch_and_tree('subdir')
299
 
        # writelock the tree so its repository doesn't get readlocked by
300
 
        # the revision tree locks. This works around the bug where we dont
301
 
        # permit lock upgrading.
302
 
        subtree.lock_write()
303
 
        self.addCleanup(subtree.unlock)
304
 
        self.build_tree(['subdir/file-a',])
305
 
        subtree.add(['file-a'], ['id-a'])
306
 
        rev1 = subtree.commit('commit in subdir')
307
 
        rev1_tree = subtree.basis_tree()
308
 
        rev1_tree.lock_read()
309
 
        self.addCleanup(rev1_tree.unlock)
310
 
 
311
 
        subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
312
 
        self.build_tree(['subdir2/file-b'])
313
 
        subtree2.add(['file-b'], ['id-b'])
314
 
        rev2 = subtree2.commit('commit in subdir2')
315
 
        rev2_tree = subtree2.basis_tree()
316
 
        rev2_tree.lock_read()
317
 
        self.addCleanup(rev2_tree.unlock)
318
 
 
319
 
        subtree.merge_from_branch(subtree2.branch)
320
 
        rev3 = subtree.commit('merge from subdir2')
321
 
        rev3_tree = subtree.basis_tree()
322
 
        rev3_tree.lock_read()
323
 
        self.addCleanup(rev3_tree.unlock)
324
 
 
325
 
        repo = tree.branch.repository
326
 
        repo.fetch(subtree.branch.repository, rev3)
327
 
        # will also pull the others...
328
 
 
329
 
        # tree doesn't contain a text merge yet but we'll just
330
 
        # set the parents as if a merge had taken place. 
331
 
        # this should cause the tree data to be folded into the 
332
 
        # dirstate.
333
 
        tree.set_parent_trees([
334
 
            (rev1, rev1_tree),
335
 
            (rev2, rev2_tree),
336
 
            (rev3, rev3_tree), ])
337
 
 
338
 
        # now we should be able to get them back out
339
 
        self.assertTreesEqual(tree.revision_tree(rev1), rev1_tree)
340
 
        self.assertTreesEqual(tree.revision_tree(rev2), rev2_tree)
341
 
        self.assertTreesEqual(tree.revision_tree(rev3), rev3_tree)
342
 
 
343
 
    def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
344
 
        """Setting parent trees on a dirstate working tree takes
345
 
        the trees it's given and doesn't need to read them from the 
346
 
        repository.
347
 
        """
348
 
        tree = self.make_workingtree()
349
 
 
350
 
        subtree = self.make_branch_and_tree('subdir')
351
 
        rev1 = subtree.commit('commit in subdir')
352
 
        rev1_tree = subtree.basis_tree()
353
 
        rev1_tree.lock_read()
354
 
        self.addCleanup(rev1_tree.unlock)
355
 
 
356
 
        tree.branch.pull(subtree.branch)
357
 
 
358
 
        # break the repository's legs to make sure it only uses the trees
359
 
        # it's given; any calls to forbidden methods will raise an 
360
 
        # AssertionError
361
 
        repo = tree.branch.repository
362
 
        repo.get_revision = self.fail
363
 
        repo.get_inventory = self.fail
364
 
        repo.get_inventory_xml = self.fail
365
 
        # try to set the parent trees.
366
 
        tree.set_parent_trees([(rev1, rev1_tree)])
367
 
 
368
 
    def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
369
 
        """Getting parent trees from a dirstate tree does not read from the 
370
 
        repos inventory store. This is an important part of the dirstate
371
 
        performance optimisation work.
372
 
        """
373
 
        tree = self.make_workingtree()
374
 
 
375
 
        subtree = self.make_branch_and_tree('subdir')
376
 
        # writelock the tree so its repository doesn't get readlocked by
377
 
        # the revision tree locks. This works around the bug where we dont
378
 
        # permit lock upgrading.
379
 
        subtree.lock_write()
380
 
        self.addCleanup(subtree.unlock)
381
 
        rev1 = subtree.commit('commit in subdir')
382
 
        rev1_tree = subtree.basis_tree()
383
 
        rev1_tree.lock_read()
384
 
        self.addCleanup(rev1_tree.unlock)
385
 
        rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
386
 
        rev2_tree = subtree.basis_tree()
387
 
        rev2_tree.lock_read()
388
 
        self.addCleanup(rev2_tree.unlock)
389
 
 
390
 
        tree.branch.pull(subtree.branch)
391
 
 
392
 
        # break the repository's legs to make sure it only uses the trees
393
 
        # it's given; any calls to forbidden methods will raise an 
394
 
        # AssertionError
395
 
        repo = tree.branch.repository
396
 
        # dont uncomment this: the revision object must be accessed to 
397
 
        # answer 'get_parent_ids' for the revision tree- dirstate does not 
398
 
        # cache the parents of a parent tree at this point.
399
 
        #repo.get_revision = self.fail
400
 
        repo.get_inventory = self.fail
401
 
        repo.get_inventory_xml = self.fail
402
 
        # set the parent trees.
403
 
        tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
404
 
        # read the first tree
405
 
        result_rev1_tree = tree.revision_tree(rev1)
406
 
        # read the second
407
 
        result_rev2_tree = tree.revision_tree(rev2)
408
 
        # compare - there should be no differences between the handed and 
409
 
        # returned trees
410
 
        self.assertTreesEqual(rev1_tree, result_rev1_tree)
411
 
        self.assertTreesEqual(rev2_tree, result_rev2_tree)
412
 
 
413
 
    def test_dirstate_doesnt_cache_non_parent_trees(self):
414
 
        """Getting parent trees from a dirstate tree does not read from the 
415
 
        repos inventory store. This is an important part of the dirstate
416
 
        performance optimisation work.
417
 
        """
418
 
        tree = self.make_workingtree()
419
 
 
420
 
        # make a tree that we can try for, which is able to be returned but
421
 
        # must not be
422
 
        subtree = self.make_branch_and_tree('subdir')
423
 
        rev1 = subtree.commit('commit in subdir')
424
 
        tree.branch.pull(subtree.branch)
425
 
        # check it fails
426
 
        self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
427
 
 
428
 
    def test_no_dirstate_outside_lock(self):
429
 
        # temporary test until the code is mature enough to test from outside.
430
 
        """Getting a dirstate object fails if there is no lock."""
431
 
        def lock_and_call_current_dirstate(tree, lock_method):
432
 
            getattr(tree, lock_method)()
433
 
            tree.current_dirstate()
434
 
            tree.unlock()
435
 
        tree = self.make_workingtree()
436
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
437
 
        lock_and_call_current_dirstate(tree, 'lock_read')
438
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
439
 
        lock_and_call_current_dirstate(tree, 'lock_write')
440
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
441
 
        lock_and_call_current_dirstate(tree, 'lock_tree_write')
442
 
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
443
 
 
444
 
    def test_new_dirstate_on_new_lock(self):
445
 
        # until we have detection for when a dirstate can be reused, we
446
 
        # want to reparse dirstate on every new lock.
447
 
        known_dirstates = set()
448
 
        def lock_and_compare_all_current_dirstate(tree, lock_method):
449
 
            getattr(tree, lock_method)()
450
 
            state = tree.current_dirstate()
451
 
            self.assertFalse(state in known_dirstates)
452
 
            known_dirstates.add(state)
453
 
            tree.unlock()
454
 
        tree = self.make_workingtree()
455
 
        # lock twice with each type to prevent silly per-lock-type bugs.
456
 
        # each lock and compare looks for a unique state object.
457
 
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
458
 
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
459
 
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
460
 
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
461
 
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
462
 
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
463
 
 
464
 
 
465
223
class TestWorkingTreeFormatAB1(TestCaseWithTransport):
466
224
    """Tests specific to WorkingTreeFormat3."""
467
225
 
516
274
 
517
275
    def create_format2_tree(self, url):
518
276
        return self.make_branch_and_tree(
519
 
            url, format=bzrlib.bzrdir.BzrDirFormat6())
 
277
            url, format=bzrdir.BzrDirFormat6())
520
278
 
521
279
    def test_conflicts(self):
522
280
        # test backwards compatability