220
220
self.assertEqual([], tree.get_parent_ids())
223
class TestWorkingTreeFormat4(TestCaseWithTransport):
224
"""Tests specific to WorkingTreeFormat4."""
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)
232
# format 'Bazaar Working Tree format 4'
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())
248
def test_uses_lockdir(self):
249
"""WorkingTreeFormat4 uses its own LockDir:
251
- lock is a directory
252
- when the WorkingTree is locked, LockDir can see that
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)
264
self.assertTrue(our_lock.peek())
266
self.assertEquals(our_lock.peek(), None)
268
def make_workingtree(self):
270
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
271
repo = dir.create_repository()
272
branch = dir.create_branch()
274
return workingtree.WorkingTreeFormat4().initialize(dir)
275
except errors.NotLocalUrl:
276
raise TestSkipped('Not a local URL')
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.)
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.
287
def test_dirstate_stores_all_parent_inventories(self):
288
tree = self.make_workingtree()
290
# We're going to build in tree a working tree
291
# with three parent trees, with some files in common.
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.
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.
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)
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)
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)
325
repo = tree.branch.repository
326
repo.fetch(subtree.branch.repository, rev3)
327
# will also pull the others...
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
333
tree.set_parent_trees([
336
(rev3, rev3_tree), ])
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)
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
348
tree = self.make_workingtree()
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)
356
tree.branch.pull(subtree.branch)
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
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)])
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.
373
tree = self.make_workingtree()
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.
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)
390
tree.branch.pull(subtree.branch)
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
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)
407
result_rev2_tree = tree.revision_tree(rev2)
408
# compare - there should be no differences between the handed and
410
self.assertTreesEqual(rev1_tree, result_rev1_tree)
411
self.assertTreesEqual(rev2_tree, result_rev2_tree)
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.
418
tree = self.make_workingtree()
420
# make a tree that we can try for, which is able to be returned but
422
subtree = self.make_branch_and_tree('subdir')
423
rev1 = subtree.commit('commit in subdir')
424
tree.branch.pull(subtree.branch)
426
self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
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()
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)
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)
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')
465
223
class TestWorkingTreeFormatAB1(TestCaseWithTransport):
466
224
"""Tests specific to WorkingTreeFormat3."""